考虑以下课程:
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());
我知道它不起作用,因为它应该只能将Notifiable
的一个具体子类型添加到set2
,但它是如何实现的,以下代码也不起作用?
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;
答案 0 :(得分:3)
第一个代码完全有效。因为您可以拥有超类型的引用,所以保存子类对象,如下面的赋值:
Notifiable notifiable = new NotifiableImpl1();
您可以将NotifiableImpl
对象添加到Set<Notifiable>
。该集合可以包含任何子类型的对象。
根据PECS rule,第二个代码无效。基本上Set<? extends Notifiable>
是Notifiable
的生产者,而不是消费者。您不能向其添加任何子类型对象。该集实际上可以保存对HashSet<NotifiableImpl1>
的引用,并且您无法向此类集添加NotifiableImpl2
对象。如果编译器允许,它将在运行时失败。实际上,除了null
之外,您不能向这些类型添加任何内容,因为编译器仍然不知道Set
将保留的实际类型。
第三个代码也有效。 Set<? extends Notifiable>
表示从Set
延伸的未知类型的Notifiable
。因此,在以下代码中:
Set<Notifiable> set = new HashSet<>();
Set<? extends Notifiable> set3 = set;
您只是将具体的参数化类型引用分配给有界通配符类型。由于Notifiable
是? extends Notifiable
的有效替代,因此该分配非常有意义。
存在通配符类型背后的原因是允许单个引用点指向不同类型的对象。这是一般的多态规则。如果没有通配符,Set<Notifiable>
指向HashSet<NotifiableImpl1>
将无效,只是因为泛型类型是不变的。