我怀疑此处已被问及(并已回答),但我不知道如何命名问题。为什么只有在我没有通过课程本身时才能毫无问题地表达通配符?
这一切归结为此代码。除了拨打genericsHell(ShapeSaver.class)
之外,一切都按预期工作:
interface Shape { }
interface Circle extends Shape { }
interface ShapeProcessor<T extends Shape> { }
class CircleDrawer implements ShapeProcessor<Circle> { }
class ShapeSaver<T extends Shape> implements ShapeProcessor<T> { }
class Test {
void genericsHeaven(ShapeProcessor<? extends Shape> a) {}
void genericsHell(Class<? extends ShapeProcessor<? extends Shape>> a) {}
void test() {
genericsHeaven(new CircleDrawer());
genericsHeaven(new ShapeSaver<Circle>());
genericsHell(CircleDrawer.class);
genericsHell(ShapeSaver.class); // ERROR: The method genericsHell is not applicable for the arguments (Class<ShapeSaver>)
}
}
答案 0 :(得分:17)
ShapeSaver.class
的类型为Class<ShapeSaver>
。将其提供给genericsHell()
时,编译器需要检查Class<ShapeSaver>
是否为Class<? extends ShapeProcessor<?>
的子类型,这会减少ShapeSaver
是否为ShapeProcessor<?>
的子类型。子类型关系不成立,方法调用失败。
同样的事情应该适用于@ Bohemian的解决方案。这里子类型检查发生在推断T
之后T
的绑定检查时。它也应该失败。这似乎是一个编译器错误,它以某种方式错误解释了Raw
可分配给Raw<X>
的规则,就像Raw
是Raw<X>
的子类型一样。另见Enum.valueOf throws a warning for unknown type of class that extends Enum?
解决问题的一个简单方法是声明
void genericsHell(Class<? extends ShapeProcessor> a)
的确,ShapeSaver
是ShapeProcessor
的子类型,并且调用会编译。
这不仅仅是一种解决方法。这是有充分理由的。严格来说,对于任何Class<X>
,X
必须是原始类型。例如,Class<List>
没问题,Class<List<String>>
没有。因为实际上没有代表List<string>
的类;只有一个代表List
的类。
忽略严厉警告,不要使用原始类型。考虑到Java类型系统的设计方式,我们有时必须使用原始类型。甚至Java的核心API(Object.getClass()
)也使用原始类型。
您可能打算做这样的事情
genericsHell(ShapeSaver<Circle>.class);
不幸的是,这是不允许的。 Java可能有,但没有,引入类型文字和泛型。这给很多库带来了很多问题。 java.lang.reflect.Type
是一团糟,无法使用。每个库都必须引入自己的类型系统表示来解决问题。
你可以借一个,例如来自Guice,你将能够
genericsHell( new TypeLiteral< ShapeSaver<Circle> >(){} )
------------------
(在阅读代码时学会跳过ShaveSaver<Circle>
周围的掷骰子)
在genericsHell()
的方法正文中,您将获得完整的类型信息,而不仅仅是类。
答案 1 :(得分:11)
键入genericsHell
方法可以编译:
static <T extends ShapeProcessor<?>> void genericsHell(Class<T> a) {}
EDITED:这允许编译器从上下文或通过编码显式类型指定ShapeProcessor
不是字面任何 ShapeProcessor
,而是与作为参数传递的类型相同的类型。如果调用是显式类型的,(编译器在封面下执行),代码将如下所示:
MyClass.<ShapeSaver>genericsHell(ShapeSaver.class);
有趣的是,它提供了一个类型警告,但仍然可以编译。但是,不需要显式类型,因为参数中有足够的类型信息可用于infer泛型类型。
你的问题遗漏了一些声明,所以我添加了它们来创建一个Short Self-Contained Correct Example - 即这段代码按原样编译
static interface Shape { }
static interface Circle extends Shape { }
static interface ShapeProcessor<T extends Shape> { }
static class CircleDrawer implements ShapeProcessor<Circle> { }
static class ShapeSaver<T extends Shape> implements ShapeProcessor<T> { }
static void genericsHeaven(ShapeProcessor<? extends Shape> a) { }
// The change was made to this method signature:
static <T extends ShapeProcessor<?>> void genericsHell(Class<T> a) { }
static void test() {
genericsHeaven(new CircleDrawer());
genericsHeaven(new ShapeSaver<Circle>());
genericsHell(CircleDrawer.class);
genericsHell(ShapeSaver.class);
}