我有一个遗留类,该类本身不是通用的,但其方法之一返回类型使用泛型:
public class Thing {
public Collection<String> getStuff() { ... }
}
getStuff()
使用泛型返回字符串集合。因此,我可以迭代getStuff()
,并且无需将元素强制转换为String
:
Thing t = new Thing();
for (String s: t.getStuff()) // valid
{ ... }
但是,如果我将Thing
本身更改为通用,但保持其他所有内容相同:
public class Thing<T> {
public Collection<String> getStuff() { ... }
}
然后继续使用Thing
的非通用引用,getStuff()
不再返回Collection<String>
,而是返回非类型Collection
。因此客户端代码无法编译:
Thing t = new Thing();
for (String s: t.getStuff()) // compiler complains that Object can't be cast to String
{ ... }
这是为什么?有哪些解决方法?
我的猜测是,通过使用对泛型类的非泛型引用,Java会关闭整个类的所有泛型。这很痛苦,因为现在我通过使Thing成为通用来破坏我的客户端代码。
编辑:我正在为上面示例代码中未列出的其他方法制作Thing泛型。我的问题是关于为什么不能做到这一点的教育。
答案 0 :(得分:10)
好的,拿两个,我误解了你的问题。
当你将del Thing
(这称为原始类型)而非Thing<?>
(参数化类型)删除时,Java编译器会删除所有内容泛型参数,甚至thogh(如你的情况),方法的泛型类型与类的泛型类型无关。
来自(优秀)Java Generics FAQ:
Can I use a raw type like any other type?
原始类型的方法或构造函数具有在类型擦除后具有的签名。
这个看似无害且不引人注目的句子描述了有问题的行为。您使用Thing
作为原始类型,因此返回类型为Collection
(不是Collection<String>
),因为这是类型擦除后的类型。
困惑?不奇怪。只需看看常见问题解答的大小。地球上可能有大约三个人理解Java泛型的全部含义。只需考虑我最喜欢的JDK声明:
Enum<T extends Enum<T>>
(在常见问题解答中也有解释)。
答案 1 :(得分:0)
因擦除而失败。您可以在Java Tutorials
中详细了解相关信息答案 2 :(得分:0)
我认为这是完全正常的。在我看来,使用Thing t = new Thing();对于泛型启用类是完全错误的。当编译器看到用作没有类型参数的类的泛型类时,它认为它必须擦除该类中的所有泛型类型。这就是你如何在新的java编译器和编译器中编译旧代码而不使用泛型,让旧代码使用通用的启用类(例如java.util.ArrayList)而没有任何问题。 (这就是java不需要像C#那样分离System.Collection.Generic.List和System.Collection.List)。您可以通过添加一个简单的类型参数Thing t = new Thing();
来运行Thing<Object> t = new Thing<Object>();
,只有java编译器需要的是确保您有意识地使用java通用。我永远不能责怪Java的强大向后兼容性。
我知道我有点迟了:D