我怀疑以前曾在这里问过(并回答过),但我不知道该如何命名。为什么仅当我不通过类本身时才可以毫无问题地表达通配符?
一切都归结为这段代码。一切正常,除了对的调用genericsHell(ShapeSaver.class):
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>) } }
的类型ShapeSaver.class是Class<ShapeSaver>。当将其提供给时genericsHell(),编译器需要检查是否Class<ShapeSaver>为的子类型Class<? extends ShapeProcessor<?>,这将简化为是否ShapeSaver为的子类型ShapeProcessor<?>。子类型关系不成立,方法调用失败。
ShapeSaver.class
Class<ShapeSaver>
genericsHell()
Class<? extends ShapeProcessor<?>
ShapeSaver
ShapeProcessor<?>
@Bohemian的解决方案也应如此。在此,子类型检查在推断出T之后的边界检查时发生T。它也应该失败。这似乎是一个编译器错误,它以某种方式曲解了Raw可分配给的规则,就Raw<X>好像Raw是的子类型一样Raw<X>。
T
Raw
Raw<X>
一个简单的解决方案是声明
void genericsHell(Class<? extends ShapeProcessor> a)
确实ShapeSaver是的子类型ShapeProcessor,并且该调用会编译。
ShapeProcessor
这不只是一种解决方法。有充分的理由。严格来说,对于任何Class<X>,都X必须是原始类型。例如,Class<List>可以,Class<List<String>>不能。因为确实没有代表的阶级List<string>; 只有一个类代表List。
Class<X>
X
Class<List>
Class<List<String>>
List<string>
List
忽略将不使用原始类型的严厉警告。给定Java类型系统的设计方式,有时我们必须使用原始类型。甚至Java的核心API(Object.getClass())都使用原始类型。
Object.getClass()
您可能打算这样做
genericsHell(ShapeSaver<Circle>.class);
不幸的是,这是不允许的。Java可以但没有引入类型文字和泛型。这给很多图书馆带来了很多问题。java.lang.reflect.Type一团糟,无法使用。每个库都必须引入自己的类型系统表示法来解决该问题。
java.lang.reflect.Type
您可以向Guice借钱,例如
genericsHell( new TypeLiteral< ShapeSaver<Circle> >(){} ) ------------------
(学习ShaveSaver<Circle>在阅读代码时跳过胡扯)
ShaveSaver<Circle>
在的方法主体中genericsHell(),您将拥有完整的类型信息,而不仅仅是类。