使用通用通配符代替接口

时间:2011-04-24 15:40:20

标签: java generics interface wildcard

如果你想存储MyInterface类型的对象数组,以下两者都是可接受的,如果是这样的话你何时会使用第二种形式而不是第一种?

i)仅使用界面: -

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

ii)使用通用通配符: -

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

编辑:

到目前为止的答案已经指出,第二号将无法编译。 i和案例iii之间有什么区别: -

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

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

5 个答案:

答案 0 :(得分:7)

第二个不会编译。想象:

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>();

答案 1 :(得分:4)

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

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

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

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

修改
上面的代码将提供更灵活的API,因为Generic类型是不变的,所以如果你有 -

public class A implements MyInterface {
  // some implementation 
}

如果someMethod的参数类型为List<MyInterface>,那么它将无法使用List<A>,这会强制API用户使用{{1}类型创建List 1}}另一方面 - MyInterface允许将List<? extends MyInterface>传递给List<A> ..客户通常会有一个someMethod,因此提供一个List<ActualObject>更灵活参数将采用MyInterface的任何实现

同时使用通配符类型作为返回类型,如

public List<? extends MyInterface> someMethod();

如果一个类的用户必须考虑通配符类型,那么该类的API可能有问题。

答案 2 :(得分:4)

List<? extends MyInterface>表示某个MyInterface子类型的列表,但我们不知道哪个子类型。

由于我们不知道实际使用了哪个子类型,因此我们不能在其中放置任何对象(因为我们无法确保它是此子类型的元素)。

我们可以从这样的列表中取出对象,并且知道它是一个实现MyInterface的对象,但仅此而已。

答案 3 :(得分:2)

不同之处在于第二个不会编译。您不能使用带边界的其他类型参数来实例化类型参数。

答案 4 :(得分:0)

在实现中使用完全和完整的类型信息。

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