Why is Set<? extends Foo<?>> allowed, but Set<Foo<?>> is not
我想知道仿制药在这种情况下是如何运作的,以及为什么允许使用Set> set3 = set1;,但不允许使用Set> set2 = set1;?
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> {} |
- 关于这个"问题"的有趣阅读:stackoverflow.com/a/4343547/7709086
- @这个问题与"什么是PECS"相似,也有关联,但并不完全相同。这个问题是关于PEC的,但特别适用于类型参数本身是具有类型参数的类型的情况。这使得这是一个特别棘手的特殊案件,这就需要有自己的问题。(但如果没有其他完全重复的问题,比如在哪里,我会感到惊讶。)
简单地说,这是因为Set>是协变的(使用extends关键字)。协变类型是只读的,编译器将拒绝任何写操作,如Set.add(..)。
Set>不是协变的。它不会阻止写入或读取操作。
这个…
1 2
| Set<Foo<String>> set1 = new HashSet<>();
Set<Foo<?>> set2 = set1; // KO by compiler |
…这是非法的,因为否则我可以通过set2将Foo放入set1。
1
| set2.add(new Foo<Integer>()); // Whoopsie |
但是…
1 2
| Set<Foo<String>> set1 = new HashSet<>();
Set<? extends Foo<?>> set3 = set1; // OK |
…是协变的(extends关键字),所以它是合法的。例如,编译器将拒绝像set3.add(new Foo())这样的写操作,但接受像set3.iterator()那样的读操作。
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 foo; set2.add(foo);是因为set2.add(42)42不是Foo吗?
- 噢,谢谢你@matt,我修正了我的答案。
如果把foo的一般参数从方程中去掉,这个问题可能会更清楚。
考虑
1 2
| final Set<Foo> set1 = new HashSet<>();
Set<Object> set2 = set1; |
这使得编译错误更加明显。如果这是有效的,则可以将对象插入set2,从而违反类型约束插入set1。
1
| Set<? extends Foo> set3 = set1; |
这是完全有效的,因为set1还接受从foo派生的类型。
- 在类型删除之后,为什么要将Set>转换为Set