Java Generics Hell

我怀疑此处已被问过(并已回答),但我不知道如何命名问题。 为什么只有在我没有通过课程本身时才能表达通配符而没有问题?

这一切都归结为这段代码。 除了调用genericsHell(ShapeSaver.class)之外,一切都按预期工作:

 interface Shape { } interface Circle extends Shape { } interface ShapeProcessor { } class CircleDrawer implements ShapeProcessor { } class ShapeSaver implements ShapeProcessor { } class Test { void genericsHeaven(ShapeProcessor a) {} void genericsHell(Class<? extends ShapeProcessor> a) {} void test() { genericsHeaven(new CircleDrawer()); genericsHeaven(new ShapeSaver()); genericsHell(CircleDrawer.class); genericsHell(ShapeSaver.class); // ERROR: The method genericsHell is not applicable for the arguments (Class) } } 

ShapeSaver.class的类型是Class 。 将它提供给genericsHell() ,编译器需要检查Class是否是Class的子类型Class Class ,减少ShapeSaver是否是ShapeProcessor的子类型。 子类型关系不成立,方法调用失败。

同样的事情应该适用于@ Bohemian的解决方案。 这里,在推断T之后,在T绑定检查时发生子类型检查。 它也应该失败。 这似乎是一个编译器错误,它以某种方式错误解释了Raw可分配给Raw的规则,就像RawRaw的子类型一样。 另请参阅Enum.valueOf会针对扩展Enum的未知类型类型发出警告吗?

解决问题的简单方法是声明

 void genericsHell(Class a) 

实际上, ShapeSaverShapeSaver的子类型,并且调用编译。

这不仅仅是一种解决方法。 这是有充分理由的。 严格来说,对于任何ClassX必须是原始类型。 例如, Class是好的, Class>不是。 因为实际上没有代表List ; 只有一个代表List的类。

忽略严厉的警告,你不应该使用原始类型。 考虑到Java类型系统的设计方式,我们有时必须使用原始类型。 甚至Java的核心API( Object.getClass() )也使用原始类型。


你可能打算做这样的事情

 genericsHell(ShapeSaver.class); 

不幸的是,这是不允许的。 Java可能有,但没有,引入类型文字和generics。 这为许多库带来了很多问题。 java.lang.reflect.Type是乱七八糟的,无法使用。 每个库都必须引入自己的类型系统表示来解决问题。

你可以借一个,例如来自Guice,你就可以

 genericsHell( new TypeLiteral< ShapeSaver >(){} ) ------------------ 

(学习在阅读代码时跳过ShaveSaver周围的掷骰子)

genericsHell()的方法体中,您将拥有完整的类型信息,而不仅仅是类。

键入genericsHell方法允许它编译:

 static > void genericsHell(Class a) {} 

编辑:这允许编译器从上下文或通过编码显式类型指定ShapeProcessor不是字面上的任何 ShapeProcessor ,而是作为参数传递的类型相同的类型。 如果调用是显式类型的,(编译器在封面下执行),代码将如下所示:

 MyClass.genericsHell(ShapeSaver.class); 

有趣的是,它提供了类型警告 ,但仍然编译。 但是,不需要显式类型,因为从参数中可以获得足够的类型信息来推断generics类型。

你的问题缺少一些声明,所以我添加了它们以创建一个简短的自包含正确的示例 – 即此代码按原样编译

 static interface Shape { } static interface Circle extends Shape { } static interface ShapeProcessor { } static class CircleDrawer implements ShapeProcessor { } static class ShapeSaver implements ShapeProcessor { } static void genericsHeaven(ShapeProcessor a) { } // The change was made to this method signature: static > void genericsHell(Class a) { } static void test() { genericsHeaven(new CircleDrawer()); genericsHeaven(new ShapeSaver()); genericsHell(CircleDrawer.class); genericsHell(ShapeSaver.class); }