Why is Set<? extends Foo<?>> allowed, but Set<Foo<?>> is not
我想知道仿制药在这种情况下是如何运作的,以及为什么允许使用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | import java.util.HashSet; import java.util.Set; public class TestGenerics { public static <T> void test() { Set<T> set1 = new HashSet<>(); Set<?> set2 = set1; // OK } public static <T> void test2() { Set<Foo<T>> set1 = new HashSet<>(); Set<Foo<?>> set2 = set1; // COMPILATION ERROR Set<? extends Foo<?>> set3 = set1; // OK } } class Foo<T> {} |
简单地说,这是因为
这个…
1 2 | Set<Foo<String>> set1 = new HashSet<>(); Set<Foo<?>> set2 = set1; // KO by compiler |
…这是非法的,因为否则我可以通过
1 | set2.add(new Foo<Integer>()); // Whoopsie |
但是…
1 2 | Set<Foo<String>> set1 = new HashSet<>(); Set<? extends Foo<?>> set3 = set1; // OK |
…是协变的(
1 2 | Iterator<Foo<String>> fooIterator = set3.iterator(); // OK set3.add(new Foo<String>()); // KO by compiler |
请参阅这些文章以获得更好的解释:
- https://stackoverflow.com/a/4343547/7709086
- https://medium.freecodecamp.org/understanding-java-generic-types-covariance-and-contravariance-88f4c19763d2
如果把foo的一般参数从方程中去掉,这个问题可能会更清楚。
考虑
1 2 | final Set<Foo> set1 = new HashSet<>(); Set<Object> set2 = set1; |
这使得编译错误更加明显。如果这是有效的,则可以将对象插入set2,从而违反类型约束插入set1。
1 | Set<? extends Foo> set3 = set1; |
这是完全有效的,因为set1还接受从foo派生的类型。
除了已经给出的答案之外,我还将添加一些正式的解释。
由4.10.2(EMP.矿井)
Given a generic type declaration C (n > 0), the direct
supertypes of the parameterized type C, where Ti (1 ≤ i ≤
n) is a type, are all of the following:D < U1 θ,...,Uk θ>, where D is a generic type which is a
direct supertype of the generic type C and θ is the
substitution [F1:=T1,...,Fn:=Tn].C < S1,...,Sn> , where Si contains Ti (1 ≤ i ≤ n) (§4.5.1).
The type Object, if C is a generic interface type with no
direct superinterfaces.The raw type C.
4.5.1中规定了
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
由于
应用4.10.2,我们发现
对于第一个示例为什么不编译,可以通过假设一个矛盾来得到正式的答案。Percisely:
如果
我认为这仅仅是因为
1 2 | Set<List<T>> set3 = new HashSet<>(); Set<List<?>> set4 = set3; // compilation error due to different element datatype List<T> != List<?> |
1 2 | Set<List<T>> set4 = new HashSet<>(); Set<?> set5 = set4; // would be Ok |