类 Generic<A extends BaseType>
。就Java语言规范而言,下面这两个类型声明之间是否有显著差异?
Generic<?>
Generic<? extends BaseType>
嵌套通配符怎么办?
List<Generic<?>>
List<Generic<? extends BaseType>>
思考这一点,我认为这些应该是等价的。 Generic
指定类型参数 A
具有 BaseType
作为上限。
因此,无论我是否显式指定,通配符始终应该被 "自动" 或 "隐式" 地限制为 BaseType
。
下面,我尝试将我的直觉与 JLS 协调一致。
我找不到关于“implicit”边界的信息,所以我开始查看子类型规则。
阅读有关subtyping $4.10.2的JLS部分,它说:
给定一个泛型类型声明
C<F1,...,Fn>
(n > 0),参数化类型C<T1,...,Tn>
的直接超类型,其中 Ti(1 ≤ i ≤ n)是类型,如下所示:
D<U1 θ,...,Uk θ>
,其中D<U1,...,Uk>
是一个直接超类型为泛型类型C<T1,...,Tn>
的泛型类型,θ 是替换 [F1:=T1,...,Fn:=Tn]。
C<S1,...,Sn>
,其中 Si 包含 Ti(1 ≤ i ≤ n)(§4.5.1)。
(强调是我的)
据我所知,“通配符”在JLS中不被视为“类型”。因此,这不适用于前两个,但适用于两个 List
示例。
相反,应该适用于以下内容:
给定一个泛型类型声明 C(n > 0),其中至少有一个 Ri(1 ≤ i ≤ n)是通配符类型参数,那么参数化类型 C 的直接超类型是将捕获转换应用于 C 的结果参数化类型 C 的直接超类型(§5.1.10)。(强调我的)
将 捕获转换 $5.1.10 应用于 Generic 和 Generic;我认为我可以得到新类型变量的相同边界。在捕获转换后,我可以使用“包含”规则来建立子类型关系。
对于第一个例子,通过
如果 Ti 是 ? 形式的通配符类型参数(§4.5.1),那么 Si 是一个新的类型变量,其上限是 Ui[A1:=S1,...,An:=Sn],下限是 null 类型(§4.1)。
由于A1是BaseType
,所以新变量的上限为BaseType
。
对于第二种情况,通过以下方式:
如果Ti是形式为? extends Bi的通配符类型参数,则Si是一个新的类型变量,其上限为glb(Bi, Ui[A1:=S1,...,An:=Sn]),下限为null类型。
glb(V1,...,Vm)定义为V1&...&Vm。
我得到了glb(BaseType, BaseType)
,再次是BaseType
。
因此根据JLS,Generic<?>
和Generic<? extends BaseType>
之间的子类型关系是双向的,这与我的直觉相符。
对于嵌套通配符,我会使用"contains"规则:
类型参数T1包含另一个类型参数T2,记为T2 <= T1,如果在以下规则的自反和传递闭包下,由T2表示的类型集合可被证明是由T1表示的类型集合的子集(其中<:表示子类型关系(§4.10)):
? extends T <= ? extends S if T <: S
? extends T <= ?
? super T <= ? super S if S <: T
? super T <= ?
? super T <= ? extends Object
T <= T
T <= ? extends T
T <= ? super T
结合上面的
C<S1,...,Sn>,其中Si包含Ti(1≤i≤n)(§4.5.1)。
我得到:
List<Generic<?>>
是List<Generic<? extends BaseType>>
的直接超类型,如果Generic<?>
包含Generic<? extends BaseType>>
。
尽管如此,我不明白如何使用包含规则。根据规则,我可以使用的唯一附加信息是子类型。我已经知道两种类型之间的子类型关系是双向的。
然而,如果包含和子类型之间的关系是答案,我还可以展示List<String>
是List<Object>
的子类型,但它实际上不是也不应该是。
此外,我需要展示形式为Type <= OtherType
的内容,而唯一具有"类型"形式右侧的规则是T <= T
,所以这些规则似乎完全没有帮助。
我如何通过JLS证明List<Generic<?>>
和List<Generic<? extends BaseType>>
是彼此的子类型?
Generic
值添加到List<Generic<?>>
中而不会出现问题,但是如果您尝试将其添加到List<Generic<? extends Blah>>
中,其中Generic<T extends Blah>
,编译器会输出警告(javac 1.8.0_112)。 - Valentin RuanoList<Generic<?>
变成了List<Generic<#1 extends BaseType>>
,List<Generic<? extends BaseType>>
变成了List<Generic<#2 extends BaseType>>
,因此对于大多数操作而言,形式并不重要,但另一个问题是,哪些形式规则允许得出任何一个是另一个子类型的结论,这甚至是字面上将List<Generic<?>>
分配给List<Generic<?>>
时相同的问题。 - Holger