java为什么将is去掉_关于Java:为什么设置& lt;?扩展foo<?>gt;允许,但设置<foo<?不是...

本问题已经有最佳答案,请猛点这里访问。

我想知道仿制药在这种情况下是如何运作的,以及为什么允许使用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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值