本问题已经有最佳答案,请猛点这里访问。
我想知道仿制药在这种情况下是如何运作的,以及为什么允许使用Set extends Foo>> set3 = set1;,但不允许使用Set> set2 = set1;?
import java.util.HashSet;
import java.util.Set;
public class TestGenerics {
public static void test() {
Set set1 = new HashSet<>();
Set> set2 = set1; // OK
}
public static void test2() {
Set> set1 = new HashSet<>();
Set> set2 = set1; // COMPILATION ERROR
Set extends Foo>> set3 = set1; // OK
}
}
class Foo {}
关于这个"问题"的有趣阅读:stackoverflow.com/a/4343547/7709086
@这个问题与"什么是PECS"相似,也有关联,但并不完全相同。这个问题是关于PEC的,但特别适用于类型参数本身是具有类型参数的类型的情况。这使得这是一个特别棘手的特殊案件,这就需要有自己的问题。(但如果没有其他完全重复的问题,比如在哪里,我会感到惊讶。)
简单地说,这是因为Set extends Foo>>是协变的(使用extends关键字)。协变类型是只读的,编译器将拒绝任何写操作,如Set.add(..)。
Set>不是协变的。它不会阻止写入或读取操作。
这个…
Set> set1 = new HashSet<>();
Set> set2 = set1; // KO by compiler
…这是非法的,因为否则我可以通过set2将Foo放入set1。
set2.add(new Foo()); // Whoopsie
但是…
Set> set1 = new HashSet<>();
Set extends Foo>> set3 = set1; // OK
…是协变的(extends关键字),所以它是合法的。例如,编译器将拒绝像set3.add(new Foo())这样的写操作,但接受像set3.iterator()那样的读操作。
Iterator> fooIterator = set3.iterator(); // OK
set3.add(new Foo()); // KO by compiler
请参阅这些文章以获得更好的解释:
https://stackoverflow.com/a/4343547/7709086
https://medium.freecodecamp.org/understanding-java-generic-types-covariance-and-contravariance-88f4c19763d2
你是说Foofoo; set2.add(foo);是因为set2.add(42)42不是Foo>吗?
噢,谢谢你@matt,我修正了我的答案。
如果把foo的一般参数从方程中去掉,这个问题可能会更清楚。
考虑
final Set set1 = new HashSet<>();
Set set2 = set1;
这使得编译错误更加明显。如果这是有效的,则可以将对象插入set2,从而违反类型约束插入set1。
Set extends Foo> set3 = set1;
这是完全有效的,因为set1还接受从foo派生的类型。
在类型删除之后,为什么要将Set>转换为Set?我想通配符将被Object替换,因为它是最近的绑定?
Foo>不是Object,是Foo的"某物"。允许分配给set3的是协方差。
你的回答也意味着set3是可写的,事实并非如此。更多关于协变和逆变的信息,请访问:stackoverflow.com/a/4343547/7709086
除了已经给出的答案之外,我还将添加一些正式的解释。
由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中规定了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 ? extends T <= ? extends S if T ? extends T <= ?
? super T <= ? super S if S ? super T <= ?
? super T <= ? extends Object
T <= T
T <= ? extends T
T <= ? super T
由于T <= ? super T <= ? extends Object = ?采用了4.10.2 Foo,我们得到了? extends Foo<= ? extends Foo>。但是Foo<= ? extends Foo,所以我们有Foo<= ? extends Foo>。
应用4.10.2,我们发现Set extends Foo>>是Set>的直接超型。
对于第一个示例为什么不编译,可以通过假设一个矛盾来得到正式的答案。Percisely:
如果Set> >,我们得到的Foo<= Foo>不可能证明对4.5.1中的规则应用反身或传递关系。
我认为这仅仅是因为Set元素数据类型不同,而它必须是相同的,除了通用数据类型。第一组Set>数据类型为Foo,然后第二组Set>为Foo>,正如我看到的,元素数据类型是不同的Foo!= Foo>,而不是通用类型,因为它使用Foo,所以会导致编译错误。与以下相同无效的不同数据类型示例:
Set> set3 = new HashSet<>();
Set> set4 = set3; // compilation error due to different element datatype List != List>
Set extends Foo>> set3 = set1;可以,因为它有? datatype这是通用的,并且有目的地可以接受任何数据类型。前任:
Set> set4 = new HashSet<>();
Set> set5 = set4; // would be Ok