Java泛型,返回扩展泛型

6

为什么我不能这样做?

(涉及IT技术,需要根据上下文进一步翻译)
public abstract class A {}
public class B extends A {}
...

public ArrayList<A> foo()
{
  return new ArrayList<B>();
}

由于有很多人喜欢指出愚蠢的错误,我改成公开的了。

为什么我必须写这么多代码才能满足Java的无意义要求呢?

public List<A> foo() 
{ 
  List<A> aList = new ArrayList<A>(); 
  List<B> bList = new ArrayList<B>();
  /* fill bList*/

  for (B b : bList)
  {
    aList.add(b);
  }
  return aList;
}
3个回答

13

ArrayList<B>不是ArrayList<A>。你不能添加任意的A,例如苹果,到一个装有香蕉的ArrayList<B>里。就像一堆香蕉不是一个水果碗一样。但你可以使用通配符使其正常工作:

public ArrayList<? extends A> foo()
{
    return new ArrayList<B>();
}

请参阅Java泛型常见问题解答以获取更多详细信息。
编辑:回答您具体的问题,即为什么需要编写所有额外的代码:您不需要。只需在foo()中创建一个ArrayList<A>即可开始。没有必要将一个列表的内容复制到另一个列表中。
如果您仍然反对Java的行为,您希望以下代码发生什么?
// Doesn't compile, fortunately...
List<String> strings = new List<String>();
List<Object> objects = strings;
objects.add(new Date());
String string = strings.get(0); // Um, it's a Date, not a String...

我知道通配符,但它会破坏我的超类定义。已经在其他地方指定了“扩展”,还要指定它是愚蠢的。无论如何……如果继续争论,我就会成为一个喷子。 - Gabriel
@Gabriel:好的,你想强制该方法返回一个真正的 ArrayList<A> 吗?如果不是,可以使用通配符。如果是,请更改该方法的主体... - Jon Skeet
@Gabriel:我的回答仍然适用。我已经解释了为什么你不能将ArrayList<B>视为ArrayList<A> - 这样做不安全。 - Jon Skeet
@Jon Skeet:类型安全?你在说什么?如果B扩展了A,我可以像使用A对象一样使用B。在我发布的示例中没有任何强制转换或棘手的代码。 - Gabriel
@Gabriel:请看我的帖子结尾,那里有一个例子,说明如果你期望能够将 ArrayList<B> 视为 ArrayList<A>,仅仅因为 B 扩展了 A,会出现什么问题。 - Jon Skeet

3

a) 一方面,Java中不存在function。Java方法的格式如下:

modifiers <type_parameters[,type_parameter]*>? return_type method_name (
   [parameter[,parameter]*]?
) [throws exceptiontype[, exceptiontype]*]{ method_body }

b) 下面是如何操作:

public List<? extends A> foo()
{
  return new ArrayList<B>();
}

c)我将方法签名更改为List。如果存在适当的接口,那么在类的外部API中使用实现类型是不好的做法。


2

因为ArrayList<B>()不是ArrayList<A>,它没有从中继承过来。

B extends A并不意味着ArrayList<B>() extends ArrayList<A>()


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接