无法转换通用集合?

4

今天我遇到了一个有趣的问题。考虑以下代码:

public static class Parent {}
public static class Child extends Parent {}

Set<Child> childs = new HashSet();
Set<Parent> parents = (Set<Parent>)childs; //Error: inconvertible types

Parent parent = (Parent)new Child(); //works?!

为什么这样的转换不起作用?我希望通过引入隐式转换规则来实现类型转换,但是为什么显式转换不起作用呢?请注意,这涉及到泛型的各种规则。

如果你说 HashSet<Child>,它可能是。 - ahmet alp balkan
@ahmet 说:HashSet<Child> 在哪里? - Matt Ball
4个回答

17

强制转换无法工作,因为Java 泛型不支持协变

如果编译器允许这样做:

List<Child> children = new ArrayList();
List<Parent> parents = (List<Parent>)children;

那么在这种情况下会发生什么?

parents.add(new Parent());
Child c = children.get(0);
最后一行尝试将Parent赋值给Child,但是Parent不是一个Child 所有的Child都是Parent(因为Child extends Parent),但并非所有的Parent都是Child

这是一个有趣的观点。然而,这对我的API造成了混乱。感谢您的回答。 - TheLQ
没问题。如果情况很糟糕,而且你不知道如何正确地将 API 参数化,请提出另一个问题,并附上一些具体的示例,我会帮你看看。 - Matt Ball

1
一个 Set<Parent> 可以包含任何 Parent 的子类。一个 Set<Child> 可以包含任何 Child 的子类。因此,一个既不是 Child 子类又不是 Parent 子类的子类将被允许存在于其中一个中,但不能同时存在于两个中,这使它们成为不兼容的类型。

1
因为子集不是父集。定义一个泛型(参数化)集合与定义一个新类本质上是一样的,因此您正在定义两个不相交的类。

1

尽管如此,泛型的超类型工作方式略有不同:

Set<Child> childs = new HashSet();
Set<? extends Parent> parents = childs;

Set是set和Set的超类型

我建议您阅读有关泛型的文档,以了解泛型容器之间没有继承关系,仅仅因为它不是继承。继承首先意味着专业化。想象一个设计用于容纳动物的盒子,好吧,它可以由只包含老虎的盒子派生出来,但这意味着该盒子可以适用于老虎,当然,还可以做超类型所能做的所有事情,包括成为适用于所有动物的好盒子。对于人类来说,同时考虑两种分类法:上位词层次结构(专业化、继承)和部分整体层次结构(容器/被包含物),会非常混乱。

在您的最后一行中,无需进行转换:

Parent parent = new Child(); 

斯特凡


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