有没有计划让Java添加泛型集合协方差?

时间:2009-11-17 19:45:09

标签: java collections covariance

我试图写一些看起来像这样的代码:

public List<IObject> getObject(){
  ArrayList<ConcreteObject> objects = new ArrayList<ConcreteObject>();
  return objects;
}

(ConcreteObject实现IObject的地方)

这根本不起作用。它给出了编译器错误。 Java是否有计划在未来支持这一点?到那时为止,最好的解决方法是什么?我最终做的是:

public List<IObject> getObject(){
  List<IObject> objects = new ArrayList<IObject>();
  return objects;
}

这可行,也许这样做并没有任何不良副作用。这是普遍接受的最佳方法吗?

4 个答案:

答案 0 :(得分:11)

Java已经支持此功能,您只需要使用它。如需入门读物,请阅读Sun的Wildcards tutorial

您想要的是以下内容:

public List<? extends IObject> getObject(){
  ArrayList<ConcreteObject> objects = new ArrayList<ConcreteObject>();
  return objects;
}

或者,可以使用不安全的演员:

public <T extends IObject> List<T> getObject(){
  ArrayList<T> objects = (ArrayList<T>) new ArrayList<ConcreteObject>();
  return objects;
}

...但是当您尝试使用无效类型访问其元素时,此方法相当脆弱并将抛出运行时异常(发出编译错误信号除外):

@SuppressWarnings("unchecked")
public <T extends IObject> List<T> getObject(){
  ArrayList<T> objects = (ArrayList<T>) new ArrayList<ConcreteObject>();
  objects.add(new ConcreteObject());
  return objects;
}

…
List<OtherConcreteObject> objects = getObject(); // Works.
OtherConcreteObject obj = OtherConcreteObject.get(0); // Throws CCE.

这会在运行时产生以下ClassCastException:“ConcreteObject无法转换为OtherConcreteObject” - 这是相当糟糕的,因为代码在上面,应该成功。

出于这个原因,你应该尽量避免这种方法。

答案 1 :(得分:5)

出于Java Generics Tutorial

中最佳描述的原因,这是非法的

从那里做一个例子并将其应用到你的案件中:

List<ConcreteObject> concreteObjects = new ArrayList<ConcreteObject>();
List<IObject> objects = concreteObjects; // assume it's legal
objects.add(new IObject() { // anonymous or some other (incompatible with ConcreteObject) implementation
});
ConcreteObject co = concreteObjects.get(0); // Profit! er... I mean error

答案 2 :(得分:3)

为了以您期望的方式支持协方差,Java需要“具体化”的泛型。具体化的List<ConcreteObject>会知道其元素必须是ConcreteObject的实例。因此,如果一个对该列表的引用被声明为List<IObject>的调用者试图添加AnAlternateImplObject,那么该操作将在运行时失败并出现异常 - 正如数组的类似情况引发{{1}今天。

在添加泛型时,没有人能够找到一种在不破坏与现有代码兼容性的情况下实现类型的方法。提供了使用通配符的“有界”泛型类型。但是,如果我没记错的话,从那时起就设计了一种兼容的具体化方法,所以这个改变可能会在不久的将来重新出现(Java 8?)。

与此同时,您使用的解决方案是处理此案例的惯常方式。

答案 3 :(得分:1)

只是为了兴趣,如果你想用一种类似于Java的语言进行真正迂腐和严格的类型处理,你几乎不能比Scala做得更好。 Scala完全是巴洛克复杂的类型定义。 Scala的作者Odersky教授就是那个最初在Java中建立泛型(更怯懦)的人。