Java中的接口和泛型

Jon*_*an. 16 java generics interface

我有代码:

Set<? extends Notifiable> notifiables;
Run Code Online (Sandbox Code Playgroud)

通知是一个界面.我不明白上面的代码和之间的区别:

Set<Notifiable> notifiables;
Run Code Online (Sandbox Code Playgroud)

如果Notifiable是一个类,那么我就会理解它的区别,第一个代码将允许Notifiable和Notifiable的任何子类,而第二个代码只允许Notifiable(而不是任何子类)

由于您不能拥有接口实例,我可以在集合中添加/ etc?在我看来,只有两个选项,或者任何实现可通信的东西(在这种情况下,如何与第一个代码不同),或者只有"Notifiable"的实例,它们不存在,因此没有任何东西(这是毫无意义的应抛出编译时错误).

Wyz*_*a-- 15

A Set<Notifiable> 可以保存实现的类的实例Notifiable.它不仅仅限于持有具体类型的实例Notifiable(而且你是对的,没有这样的东西).但它Set<Notifiable>保证它可以包含任何类型Notifiable,因为它有一个add(Notifiable)方法可以接受任何实现接口的东西.

假设你有一些被调用的类Foo,Bar它们都实现了Notifiable.如果你创建一个Set<Foo>- 也就是说,一个允许包含Foo其子类型的实例的集合- 你不能将它传递给一个带有a的方法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>
}
Run Code Online (Sandbox Code Playgroud)

但是有时候,你要编写一个方法可以接受的事情像Set<Foo>Set<Bar>Set<Notifiable>.这就是通配符的用武之地.A 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>
}
Run Code Online (Sandbox Code Playgroud)

如果notifyAll()拿了一个Set<Notifiable>,你将无法foos转移它.

  • 关键的区别是`Set <Foo>`是*不是*Set <Notifiable>`,因为它不支持可以用来添加`Bar`的`add(Notifiable)`之类的方法.但它*是*Set <?扩展通知>`,因为这不要求它支持添加其他类型的通知. (2认同)

Ada*_*old 6

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

Set<? extends Serializable> serializables;
Run Code Online (Sandbox Code Playgroud)

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

 serializables = new HashSet<Serializable>(); // valid
 serializables = new HashSet<Number>(); // this is valid as well
 serializables = new HashSet<Integer>(); // valid
Run Code Online (Sandbox Code Playgroud)

另一方面这个:

Set<Serializable> serializables;
Run Code Online (Sandbox Code Playgroud)

只能容纳SetSerializable对象但是:

serializables = new HashSet<Serializable>();
serializables = new TreeSet<Serializable>();
Run Code Online (Sandbox Code Playgroud)

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

List<Serializable> numbers = new ArrayList<Integer>();
Run Code Online (Sandbox Code Playgroud)

推论:

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

Set<Notifiable> notifiables = new HashSet<Notifiable>();
Run Code Online (Sandbox Code Playgroud)

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

Set<? extends Notifiable> notifiables = new HashSet<MyNotifiable>();
Run Code Online (Sandbox Code Playgroud)

附录:

这是完全合法的,因此您可以根据需要改进Set后期:

Set<? extends Notifiable> notifiables = new HashSet<NotifiableA>();
notifiables  = new HashSet<NotifiableB>(); 
Run Code Online (Sandbox Code Playgroud)