Java数组与泛型的区别

4
假设有一个超类America和它的两个子类SouthAmerica和NorthAmerica。
第一种情况
对于数组:
America[] am = new SouthAmerica[10]; //why no compiler error
am[0]= new NorthAmerica();    //ArrayStoreException at RunTime

情况二

在泛型中:

ArrayList<America> ame = new ArrayList<SouthAmerica>(); //this does not compile

我的问题不是为什么第二种情况不能编译,而是为什么第一种情况可以编译。我的意思是,除了基本的数组类型和子数组对象之外,还有什么可以做到这一点?


1
相关链接:https://dev59.com/vmjWa4cB1Zd3GeqPu-Tv#12605337 - Brian
4个回答

14

这是因为数组具有协变性

这种数组行为在很大程度上被认为是一个错误:

String[] strings = new String[1];
Object[] objects = strings;
objects[0] = new Integer(1); // RUN-TIME FAILURE

通用集合 - 作为较新的设计,已修复该错误。

您可以使用有限制的通配符符号 <? super America>:

ArrayList<? super America> = new ArrayList<SouthAmerica>();

这将允许您向列表中添加项目,但将避免问题行为。
请查看官方教程中如何使用通配符的此处链接

2
这个错误是 Sun 公司为了像 Arrays.sort(any_array) 这样的方法而犯的吗? - Sachin Verma
3
我不认为这是一个错误;数组是协变的,而泛型类则不是,这是为了向后兼容而导致的。这也解释了为什么列表有一个专门的.toArray()方法,以便在运行时要求“类型安全”的数组。 - fge
3
当我提到“错误”时,我指的是事后看来的错误。我认为,如果Gosling在设计Java时考虑到泛型,数组协变就不会出现。以他的位置,设计一种没有泛型的语言似乎是合理的行为。在我之前对OP的评论中,我讨论了为什么在没有泛型的语言中拥有协变是有益的。这就是为什么其他语言如C#也采用了类似的做法,明确意识到它的问题性质,但同时也解决了它所带来的问题。 - Benjamin Gruenbaum
2
@BenjaminGruenbaum 嗯,.NET语言有机会从Java的错误中学习;但更广泛地说,Java语言设计者在某个时候必须打破向后兼容性;如果失败,将来推广Java将变得不可持续。 - fge
@PaulBellora 天啊,那个问题居然被忽略了整整一天(附上官方教程链接,解释为什么它不起作用,并在下面提供一个示例说明为什么不应该这样做)?谢谢! - Benjamin Gruenbaum
显示剩余2条评论

3
您正在做一些错误的事情。根据您所描述的,我们假设有以下内容:
public class Sample {
    public static void main() {
    America am = new SouthAmerica[10];
    }
}
class America {
    private Integer Size;
}
class NorthAmerica extends America {
    private Integer USA;
}
class SouthAmerica extends America {
    private Integer Chile;
}

我尝试编译上述类,但它遇到了错误。
javac Sample.java. 
Sample.java:3: incompatible types
found   : SouthAmerica[]
required: America
America am = new SouthAmerica[10];
1 error

我只是在参考OP的陈述,他提到它可以编译但实际上不行。 - Deepak

2
因为数组是协变的,而集合是逆变的。这意味着String[]Object[],但List<String>不是List<Object>。因此您应该写成以下形式:
List<? extends America> list = new ArrayList<SouthAmerica>(); 

2

案例一: 我猜你的意思是:

America[] am = new SouthAmerica[10]; //why no compiler error

这是有效的,因为am可以持有SouthAmerica数组,因为SouthAmerica扩展了America。

第二行无效,因为数组是SouthAmerica类型,而你试图设置一个NorthAmerica。SouthAmerica不是NorthAmerica的超类。

第一个有效的原因是,之后你可以合法地说:

am=new NorthAmerica[5];

这全部归因于数组的协方差属性。
情况2:尽管南美洲是美洲的一部分,但通用性并不适用。ArrayList不会像暗示那样扩展ArrayList。泛型(即使在这种情况下它是一个ArrayList)的行为方式与完整数组不同。

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