Java中的接口和泛型

内容来源于 Stack Overflow,并遵循CC BY-SA 3.0许可协议进行翻译与使用

  • 回答 (2)
  • 关注 (0)
  • 查看 (12)

我有这样的代码:

Set<? extends Notifiable> notifiables;

应通知的是一个接口。我不明白上面的代码和:

Set<Notifiable> notifiables;

如果Notifiable是一个类,那么我会理解这种差异,第一个代码将允许Notifiable和任何Notifiable的子类,而第二个代码只允许Notifiable(而不是任何子类)

因为你不能有一个接口的实例,我可以添加/ etc到集合中?在我看来,只有两种选择,不管是实现Notifiable的东西(在这种情况下,与第一个代码有什么不同),或者只有“Notifiable的实例”,它们不能存在,所以什么都没有(这是没有意义的,应该会引发编译时错误)。

提问于
用户回答回答于

让我们用一个更直接的例子:

Set<? extends Serializable> serializables;

这一个声明一个变量,它可以保存SetIntegers,Floats等的引用:

 serializables = new HashSet<Serializable>(); // valid
 serializables = new HashSet<Number>(); // this is valid as well
 serializables = new HashSet<Integer>(); // valid

另一方面:

Set<Serializable> serializables;

只能容纳SetSerializable对象但是:

serializables = new HashSet<Serializable>();
serializables = new TreeSet<Serializable>();

所以这一个将是一个编译器错误:

List<Serializable> numbers = new ArrayList<Integer>();

推论:

如果你想要一个可以容纳任何子类型的 字段Notifiable然后使用:

Set<Notifiable> notifiables = new HashSet<Notifiable>();

如果你想限制Notifiable可以使用的子类型,那么这是一条路:

Set<? extends Notifiable> notifiables = new HashSet<MyNotifiable>();

附录:

这是完全合法的,所以你可以Set在你认为合适的时候进行改装:

Set<? extends Notifiable> notifiables = new HashSet<NotifiableA>();
notifiables  = new HashSet<NotifiableB>(); 
用户回答回答于

A Set<Notifiable> 可以容纳实现的类的实例Notifiable。它并不局限于只持有具体类型的实例Notifiable(并且你是对的,没有这种东西)。但它Set<Notifiable>保证它可以拥有任何类型的Notifiable,因为它有一个add(Notifiable)方法可以接受任何实现接口的方法。

假设你有一些调用的类FooBar并且都实现了Notifiable。如果你创建了一个Set<Foo>- 也就是允许包含实例Foo及其子类型的集合- 你不能将它传递给带有的方法Set<Notifiable>,因为该方法可能会添加非Foo实例的东西,比如Bar

public void addABar(final Set<Notifiable> notifiables) {
    notifiables.add(new Bar());  // OK, since Bar is a subtype of Notifiable
}

public void wontWork() {
  final Set<Foo> foos = new HashSet<>();
  addABar(foos);  // Compile error, can't convert Set<Foo> to Set<Notifiable>
}

但是有时候,你要编写一个方法可以接受的事情像Set<Foo>Set<Bar>Set<Notifiable>。这就是通配符出现的地方。Set<? extends Notifiable>保证所有内容都是某种类型的Notifiable,但并不能保证Notifiable可以添加任何类型的内容; 它可以被限制为一个子类型。您不能调用add()它,因为该方法现在add(? extends Notifiable)不是,add(Notifiable)而且您无法调用其参数类型未知的方法。

当你不需要添加元素时,你通常会使用它,但你需要查看现有的元素并调用Notifiable它们的接口方法,并且你希望允许调用者传递一些子类型的集合,例如Set<Foo>

例如:

public void notifyAll(final Set<? extends Notifiable> notifiables) {
    for (final Notifiable notifiable : notifiables) {
        notifiable.notify();
    }
}

public void example() {
    final Set<Foo> foos = whatever();
    notifyAll(foos);  // OK, since a Set<Foo> is a Set<? extends Notifiable>
}

如果notifyAll()拿了一个Set<Notifiable>,你将无法传递foos给它。

扫码关注云+社区