gpt4 book ai didi

java - 明确指定通配符的上限时有区别吗?

转载 作者:IT老高 更新时间:2023-10-28 20:42:16 27 4
gpt4 key购买 nike

假设我有一个通用的 class Generic<A extends BaseType> .

就 Java 语言规范而言,以下两种类型声明之间是否存在显着差异?

Generic<?>
Generic<? extends BaseType>

嵌套通配符呢?
List<Generic<?>>
List<Generic<? extends BaseType>>

考虑到这一点,我认为这些是等效的。 Generic指定类型参数 ABaseType为一个上限。

因此,通配符应该总是以 BaseType“自动”或“隐式”为界。 ,无论我是否明确指定。

下面,我尝试将我的直觉与 JLS 调和。

我找不到关于“隐式”边界的信息,所以我从查看子类型规则开始。

阅读关于 subtyping $4.10.2 的 JLS 部分, 它说:

Given a generic type declaration C<F1,...,Fn> (n > 0), the direct supertypes of the parameterized type C<T1,...,Tn>, where Ti (1 ≤ i ≤ n) is a type, are all of the following:

  • D<U1 θ,...,Uk θ>, where D<U1,...,Uk> is a generic type which is a direct supertype of the generic type C<T1,...,Tn> and θ is the substitution [F1:=T1,...,Fn:=Tn].

  • C<S1,...,Sn>, where Si contains Ti (1 ≤ i ≤ n) (§4.5.1).



(强调我的)

据我了解,“通配符”在 JLS 中不被视为“类型”。所以这不适用于前两个,但适用于两个 List例子。

相反,这应该适用:

Given a generic type declaration C<F1,...,Fn> (n > 0), the direct supertypes of the parameterized type C<R1,...,Rn> where at least one of the Ri (1 ≤ i ≤ n) is a wildcard type argument, are the direct supertypes of the parameterized type C<X1,...,Xn> which is the result of applying capture conversion to C<R1,...,Rn> (§5.1.10).



(强调我的)

申请 capture conversion $5.1.10Generic<?>Generic<? extends BaseType> ;我想我在新类型变量上得到了相同的界限。捕获转换后,我可以使用“包含”规则来建立子类型。

对于第一个示例,通过

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).



由于 A1 是 BaseType ,新鲜变量的上限为 BaseType .

对于第二种情况,通过

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.



我得到 glb(BaseType, BaseType) ,同样是 BaseType .

所以看起来 Generic<?>之间的子类型关系和 Generic<? extends BaseType>根据 JLS,这是双向的,这符合我的直觉。

对于嵌套通配符,我会使用 "contains" rule :

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<S1,...,Sn>, where Si contains Ti (1 ≤ i ≤ n) (§4.5.1).



从上面,我得到:
List<Generic<?>>List<Generic<? extends BaseType>> 的直接父类(super class)型如果 Generic<?>包含 Generic<? extends BaseType>>
虽然,我不明白我是如何使用 contains 规则的。根据规则,我可以使用的唯一附加信息是子类型。我已经知道子类型在这两种类型之间是双向的。

虽然,如果包含两者之间的子类型是答案,我也可以证明 List<String>List<Object> 的子类型它不是也不应该是。

此外,我需要展示形式为 Type <= OtherType 的东西。唯一具有“类型”形式右侧的规则是 T <= T ,所以这些规则似乎根本没有帮助。

我如何得到那个 List<Generic<?>>List<Generic<? extends BaseType>>是通过 JLS 彼此的亚型吗?

最佳答案

从字面上看您最初的问题,Generic<?> 之间是否“是否存在显着差异”?和 Generic<? extends BaseType> ,答案一定是,它们不等价。

JLS §4.5.1明确指出:

The wildcard ? extends Object is equivalent to the unbounded wildcard ?.



所以它等价于 ? extends BaseType仅当 BaseTypeObject ,但即便如此,它们也是等价的,但仍然存在显着差异,例如在没有捕获转换发生的地方:
boolean b1 = new Object() instanceof Supplier<?>; // valid code
boolean b2 = new Object() instanceof Supplier<? extends Object>; // invalid

Supplier<?>[] array1; // valid declaration
Supplier<? extends Object>[] array1; // invalid

可能值得注意的是,与第一个直觉相反,给定声明 Generic<T extends BaseType> , 指定 Generic<? extends Object>与等效的 Generic<?> 一样有效.只要通配符的边界不能证明与类型参数的边界不同,并且边界总是 Object 的子类型,通配符的边界就是有效的。 , ? extends Object始终有效。

所以如果我们有一个像这样的类型声明
interface NumberSupplier<N extends Number> extends Supplier<N> {}

我们可以写
NumberSupplier<? extends Object> s1;
NumberSupplier<? extends Serializable> s2;
NumberSupplier<? extends BigInteger> s3;

甚至
NumberSupplier<? extends CharSequence> s4;

我们甚至可以在没有扩展 Number 的实际类型的情况下实现它和 CharSequence使用 () -> null
但不是
NumberSupplier<? extends String> s5;

StringNumber可以证明是不同的。

说到赋值,我们可以使用问题中已经引用的子类型规则得出结论 NumberSupplier<? extends BigInteger>NumberSupplier<? extends Object> 的子类型, 因为 ? extends BigInteger包含 ? extends Object (并且还包含 ? extends Number ),因为 BigIntegerObject 的子类型和 Number ,但正如您正确指出的那样,这不适用于类型参数不是通配符的参数化类型。

所以如果我们有像 List<NumberSupplier<?>> 这样的声明, List<NumberSupplier<? extends Object>> , 或 List<NumberSupplier<? extends Number>>并且想要根据 §4.5.1 的包含规则推理是否是其他的子类型,唯一可以应用的规则是,当类型参数是相同类型( T <= T )时,但是,我们不会不需要子类型规则,所有这些列表类型 are the same type :

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 规则仍然有用,例如它允许得出结论 Map<String,? extends Number>Map<String,Integer> 的子类型, 因为对于第一个类型参数 String <= String适用并且第二个类型参数的类型参数由通配符特定的包含规则覆盖。

所以剩下的问题是,哪个规则允许我们得出结论 NumberSupplier<?> , NumberSupplier<? extends Object> , 或 NumberSupplier<? extends Number>是相同的类型,所以 List<NumberSupplier<?>> , List<NumberSupplier<? extends Object>> , 或 List<NumberSupplier<? extends Number>>可以相互分配。

它似乎不是捕获转换,因为捕获转换意味着计算有效边界,但也会为每个通配符创建一个“新类型变量”,这肯定是不同的类型。但是没有其他规则涵盖通配符兼容性。或者我没找到。试图将规范与 javac 的实际行为相匹配有一些非常有趣的结果:

给定的
interface MySupplier<S extends CharSequence&Appendable> extends Supplier<S> {}

以下声明显然是有效的:
List<MySupplier<? extends CharSequence>> list1 = Collections.emptyList();
List<MySupplier<? extends Appendable>> list2 = Collections.emptyList();

因为在这两种情况下,通配符的边界与 S 中的一个匹配是多余的。的界限,我们可能会猜测它们实际上是同一类型。

但是 javac认为他们不是
list1 = list2; // compiler error
list2 = list1; // dito

尽管任何涉及捕获转换的操作都会得出兼容的类型,例如
list1.set(0, list2.get(0)); // no problem
list2.set(0, list1.get(0)); // no problem

并间接执行被拒绝的任务:
List<MySupplier<?>> list3;
list3 = list1;
list2 = list3; // no problem
// or
list3 = list2;
list1 = list3; // again no problem

但在这里, ?不等于 ? extends Object :
List<MySupplier<? extends Object>> list4;
list4 = list1; // compiler error
list2 = list4; // dito
// or
list4 = list2; // dito
list1 = list4; // dito

但同样,间接分配工作。
list4 = list3 = list1; // works
list1 = list3 = list4; // works
list4 = list3 = list2; // works
list2 = list3 = list4; // works

所以不管规则 javac在这里使用,它不是可传递的,这排除了子类型关系,以及一般的“它是同一类型”规则。看来,这确实是未(未)指定的,并且直接影响了实现。而且,正如目前实现的那样, ? without bounds 是特殊的,它允许使用任何其他通配符类型都无法实现的赋值链。

关于java - 明确指定通配符的上限时有区别吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/48170539/

27 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com