使用通用通配符代替接口

2022-09-04 19:43:40

如果要存储类型为 的对象数组,以下两者是否可以接受,如果是这样,您将何时使用第二种形式而不是第一种形式?MyInterface

i) 仅使用接口:-

List<MyInterface> mylist = new ArrayList<MyInterface>();

ii) 使用通用通配符:-

List<? extends MyInterface> mylist = new ArrayList<? extends MyInterface>();

编辑:

正如到目前为止的答案所指出的那样,数字ii不会编译。i和案例iii有什么区别:-

iii) 仅在引用中使用通用通配符:-

List<? extends MyInterface> mylist = new ArrayList<MyInterface>();

答案 1

第二个不会编译。想象:

A implements MyInterface
B implements MyInterface

然后,以下内容将匹配您的第二个表达式,但不会编译:

// incorrect
List<A> mylist = new ArrayList<B>();

更正:一个也错了

List<? extends MyInterface> mylist = new ArrayList<MyInterface>();

从某种意义上说,它确实可以编译,但你不能向它添加MyInterface的任何子类。令人困惑,但正确 - 在我阅读了解释之后。同样的原因:通配符可以被视为例如:

// I know this is not compileable; this is internal compiler "thinking".
// Read it as "somewhere someone may instantiate an ArrayList<A> and pass 
// it down to us; but we cannot accept it as something that could be 
// potentially used as List<B>"
List<A> mylist = new ArrayList<MyInterface>();

所以这行不通:

mylist.add(b);

反之亦然。编译器拒绝执行这些可能不正确的操作。

允许您将 MyInterface 的任何子类添加到 mylist 的选项是:

List<MyInterface> mylist = new ArrayList<MyInterface>();

答案 2

如果您想存储类型的对象,那么更好(且可编译)的方法将是 -MyInterface

List<MyInterface> mylist = new ArrayList<MyInterface>();

但是,如果要在方法参数中使用,则可以使用选项 2(有界通配符类型)来实现 API 灵活性。

public void someMethod(List<? extends MyInterface> myInterfaces);

编辑:
上面的代码将提供更灵活的API,因为泛型类型是不变的,所以如果你有 -

public class A implements MyInterface {
  // some implementation 
}

并且如果具有参数类型,则它将无法采用 ,这会强制 API 用户在另一侧使用类型进行创建 -
将允许传递给 .。客户端通常具有,因此提供一个参数会更灵活,该参数将采用任何实现

同时不要使用通配符类型作为返回类型,例如someMethodList<MyInterface>List<A>ListMyInterfaceList<? extends MyInterface>List<A>someMethodList<ActualObject>MyInterface

public List<? extends MyInterface> someMethod();

如果类的用户必须考虑通配符类型,则该类的 API 可能存在问题。