在接口上使用通配符

时间:2014-03-23 19:08:54

标签: java generics interface bounded-wildcard

考虑以下课程:

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;

1 个答案:

答案 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>将无效,只是因为泛型类型是不变的。