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 extends ShapeProcessor>
的子类型Class extends ShapeProcessor>
Class extends ShapeProcessor>
,减少ShapeSaver
是否是ShapeProcessor>
的子类型。 子类型关系不成立,方法调用失败。
同样的事情应该适用于@ Bohemian的解决方案。 这里,在推断T
之后,在T
绑定检查时发生子类型检查。 它也应该失败。 这似乎是一个编译器错误,它以某种方式错误解释了Raw
可分配给Raw
的规则,就像Raw
是Raw
的子类型一样。 另请参阅Enum.valueOf会针对扩展Enum的未知类型类型发出警告吗?
解决问题的简单方法是声明
void genericsHell(Class extends ShapeProcessor> a)
实际上, ShapeSaver
是ShapeSaver
的子类型,并且调用编译。
这不仅仅是一种解决方法。 这是有充分理由的。 严格来说,对于任何Class
, X
必须是原始类型。 例如, 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 extends Shape> 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); }