Is there a difference when specifying upper bounds for wildcards explicitly?
就Java语言规范而言,在以下两种类型声明之间有显著的区别吗?
1 2 | Generic<?> Generic<? extends BaseType> |
嵌套通配符呢?
1 2 | List<Generic<?>> List<Generic<? extends BaseType>> |
号
考虑到这一点,我假设这些是等价的。
因此,通配符应该总是"自动"或"隐式"由
下面,我尝试将我的直觉与JLS相协调。
我找不到关于"隐式"界限的信息,所以我先看一下子类型规则。
在阅读有关4.10.2美元子类型的jls部分时,它说:
Given a generic type declaration
C (n > 0), the direct supertypes of the parameterized typeC , where Ti (1 ≤ i ≤ n) is a type, are all of the following:
D , whereD is a generic type which is a direct supertype of the generic typeC and θ is the substitution [F1:=T1,...,Fn:=Tn].
C , where Si contains Ti (1 ≤ i ≤ n) (§4.5.1).
号
(强调我的)
据我所知,在JLS中,"通配符"不被视为"类型"。所以这不适用于前两个,但它适用于两个
相反,这应该适用于:
Given a generic type declaration
C (n > 0), the direct supertypes of the parameterized typeC where at least one of the Ri (1 ≤ i ≤ n) is a wildcard type argument, are the direct supertypes of the parameterized typeC which is the result of applying capture conversion toC (§5.1.10).
号
(强调我的)
将捕获转换$5.1.10应用于
对于第一个示例,via
If Ti is a wildcard type argument (§4.5.1) of the form ?, then Si is a fresh type variable whose upper bound is Ui[A1:=S1,...,An:=Sn] and whose lower bound is the null type (§4.1).
号
由于a1is
对于第二种情况,通过
If Ti is a wildcard type argument of the form ? extends Bi, then Si is a fresh type variable whose upper bound is glb(Bi, Ui[A1:=S1,...,An:=Sn]) and whose lower bound is the null type.
glb(V1,...,Vm) is defined as V1 & ... & Vm.
号
我得到了
因此,根据jls,似乎
对于嵌套通配符,我将使用"contains"规则:
A type argument T1 is said to contain another type argument T2,
written T2 <= T1, if the set of types denoted by T2 is provably a subset of the set of types denoted by T1 under the reflexive and transitive closure of the following rules (where <: denotes subtyping (§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 , where Si contains Ti (1 ≤ i ≤ n) (§4.5.1).
号
从上面,我得到:
如果
不过,我不知道如何使用包含规则。根据规则,我唯一能使用的附加信息是子类型。我已经知道子类型在这两种类型之间是双向的。
虽然,如果两者之间包含子类型是答案,我也可以证明
此外,我还需要展示一些形式的
我如何通过jls得到
从字面上看,
JLS第4.5.1条明确规定:
The wildcard
? extends Object is equivalent to the unbounded wildcard? .
号
因此,只有当
1 2 3 4 5 |
值得注意的是,与第一直觉相反,给定声明
所以如果我们有一个类型声明
1 | interface NumberSupplier<N extends Number> extends Supplier<N> {} |
号
我们可以写信
1 2 3 | NumberSupplier<? extends Object> s1; NumberSupplier<? extends Serializable> s2; NumberSupplier<? extends BigInteger> s3; |
甚至
1 | NumberSupplier<? extends CharSequence> s4; |
。
我们甚至可以在没有实际类型扩展
但不是
1 | NumberSupplier<? extends String> s5; |
由于
当涉及到任务时,我们可以使用问题中已经引用的子类型规则来得出结论:
因此,如果我们有像
Two reference types are the same compile-time type if they have the same binary name (§13.1) and their type arguments, if any, are the same, applying this definition recursively.
号
contains规则仍然有用,例如,它允许得出这样的结论:
所以剩下的问题是,哪个规则允许我们得出这样的结论:
它似乎不是捕获转换,因为捕获转换意味着计算有效边界,但也为每个通配符创建一个"新类型变量",而该通配符肯定是不同的类型。但是没有其他规则涉及通配符兼容性。或者我没找到。试图将规范与
鉴于
1 | interface MySupplier<S extends CharSequence&Appendable> extends Supplier<S> {} |
。
以下声明显然有效:
1 2 | List<MySupplier<? extends CharSequence>> list1 = Collections.emptyList(); List<MySupplier<? extends Appendable>> list2 = Collections.emptyList(); |
。
由于在这两种情况下,通配符的绑定都是多余的,因为它们与
但
1 2 | list1 = list2; // compiler error list2 = list1; // dito |
尽管任何涉及捕获转换的操作都会得出兼容的类型,例如
1 2 | list1.set(0, list2.get(0)); // no problem list2.set(0, list1.get(0)); // no problem |
。
并且间接地做被拒绝的任务:
1 2 3 4 5 6 | List<MySupplier<?>> list3; list3 = list1; list2 = list3; // no problem // or list3 = list2; list1 = list3; // again no problem |
但在这里,
1 2 3 4 5 6 | List<MySupplier<? extends Object>> list4; list4 = list1; // compiler error list2 = list4; // dito // or list4 = list2; // dito list1 = list4; // dito |
。
但是,间接任务同样有效。
1 2 3 4 | list4 = list3 = list1; // works list1 = list3 = list4; // works list4 = list3 = list2; // works list2 = list3 = list4; // works |
号
所以不管