对generics类的非generics引用会导致非generics返回类型

我有一个遗留类,该类本身不是通用的,但其方法之一返回类型使用generics:

public class Thing { public Collection getStuff() { ... } } 

getStuff()使用generics返回字符串集合。 因此,我可以迭代getStuff()并且不需要将元素转换为String

 Thing t = new Thing(); for (String s: t.getStuff()) // valid { ... } 

但是,如果我将Thing本身更改为通用,但保持其他所有内容相同:

 public class Thing { public Collection getStuff() { ... } } 

然后继续使用对Thing的非generics引用, getStuff()不再返回Collection ,而是返回一个非类型的Collection 。 因此客户端代码不编译:

 Thing t = new Thing(); for (String s: t.getStuff()) // compiler complains that Object can't be cast to String { ... } 

为什么是这样? 解决方法有哪些?

我的猜测是,通过使用对generics类的非generics引用,Java会关闭整个类的所有generics。 这很痛苦,因为现在我通过使Thing成为通用来破坏我的客户端代码。

编辑:我正在为上面的示例代码中未列出的另一种方法制作Thinggenerics。 我的问题是关于为什么不能做到这一点的教育。

好的,拿两个,我误解了你的问题。

当您使用deling Thing (这称为原始类型 )而不是Thing参数化类型 )时,Java编译器将删除所有generics参数,甚至thogh(如您的情况),该方法的generics类型与此无关类的generics类型。

来自(优秀) Javagenerics常见问题解答 :

我可以使用任何其他类型的原始类型吗?

原始类型的方法或构造函数具有在类型擦除之后将具有的签名。

这个看似无害且不引人注意的句子描述了有问题的行为。 您使用Thing作为原始类型,因此返回类型是Collection (而不是Collection ),因为这是类型擦除后的类型。

困惑? 不奇怪。 只需看看常见问题解答的大小。 地球上可能有大约三个人理解Javagenerics的全部含义。 只考虑我最喜欢的JDK声明:

 Enum> 

(这也是FAQ中对此的解释)。

它因擦除而失败。 您可以在这些Java教程中阅读有关它的更多信息

我认为这是完全正常的。 在我看来,使用Thing t = new Thing(); 对于通用启用类是完全错误的。 当编译器看到用作没有类型参数的类的generics类时,它认为它必须擦除该类中的所有generics类型。 这就是你如何在新的java编译器和编译器中编译旧代码而不使用generics,让旧代码使用通用的启用类(例如java.util.ArrayList)而没有任何问题。 (这就是java不需要像C#那样分离System.Collection.Generic.List和System.Collection.List)。 你可以运行Thing t = new Thing(); 在其上添加一个简单的类型参数, Thingt = new Thing(); ,java编译器只需要确保你有意识地使用java通用。 我永远不能责怪Java的强大向后兼容性。

我知道我有点迟了:D