1

考虑以下类:

interface Notifiable {

}

class NotifiableImpl1 implements Notifiable {

}

class NotifiableImpl2 implements Notifiable {

}

class NotifiableImpl3 implements Notifiable {

}

以下代码正常工作:

Set<Notifiable> set = new HashSet<>();
set.add(new NotifiableImpl1());
set.add(new NotifiableImpl2());
set.add(new NotifiableImpl3());

但是,以下代码不起作用:

Set<? extends Notifiable> set2 = new HashSet<>();
set2.add(new NotifiableImpl1());
set2.add(new NotifiableImpl2());
set2.add(new NotifiableImpl3());

我知道它不起作用,因为它应该只能添加 to的一个具体子类型,但是它是怎么来的,下面的代码也不起作用?Notifiableset2

Set<? extends Notifiable> set2 = new HashSet<>();
set2.add(new NotifiableImpl1());

也许更有趣的是,为什么以下工作?

Set<Notifiable> set = new HashSet<>();
set.add(new NotifiableImpl1());
set.add(new NotifiableImpl2());
set.add(new NotifiableImpl3());

Set<? extends Notifiable> set3 = set;
4

1 回答 1

3

第一个代码是完全有效的。因为你可以有一个超类型的引用持有一个子类对象,如下面的赋值:

Notifiable notifiable = new NotifiableImpl1();

与将NotifiableImpl对象添加到Set<Notifiable>. 该集合可以包含任何子类型的对象。

根据PECS 规则,第二个代码无效。基本上 aSet<? extends Notifiable>是 的生产者Notifiable,而不是消费者。您不能向其添加任何子类型对象。该集合实际上可以包含对 a 的引用HashSet<NotifiableImpl1>,并且您不能将NotifiableImpl2对象添加到此类集合。如果编译器允许,它将在运行时失败。事实上,除了这些类型之外,你不能添加任何东西,因为编译器仍然不知道将持有null的实际类型。Set

第三个代码也是有效的。ASet<? extends Notifiable>表示Set从 扩展的未知类型的a Notifiable。因此,在以下代码中:

Set<Notifiable> set = new HashSet<>();
Set<? extends Notifiable> set3 = set;

您只是将具体的参数化类型引用分配给有界通配符类型。由于Notifiable是 的有效替代品? extends Notifiable,因此该分配非常有意义。

存在通配符类型的原因是允许单个引用指向不同类型的对象。这是一般的多态规则。Set<Notifiable>如果没有通配符, a指向 a将无效HashSet<NotifiableImpl1>,因为泛型类型是不变的。

于 2014-03-23T19:19:13.167 回答