Jef*_*rod 25 java generics nested-generics
我怀疑这里曾经问过(并回答过),但我不知道如何命名问题.为什么只有在我没有通过课程本身时才能表达通配符而没有问题?
这一切都归结为这段代码.一切都按预期工作,除了调用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>)
    }
}
irr*_*ble 17
类型ShapeSaver.class是Class<ShapeSaver>.当它被提供给genericsHell(),编译器需要检查是否Class<ShapeSaver>是一个子类型Class<? extends ShapeProcessor<?>,这减少到是否ShapeSaver是一个子类型ShapeProcessor<?>.子类型关系不成立,方法调用失败.
同样的事情应该适用于@ Bohemian的解决方案.这里子类型检查发生在推断T后的绑定检查时T.它也应该失败.这似乎是一个编译器错误,它以某种方式错误解释了Raw可分配的规则,就Raw<X>好像它Raw是一个子类型Raw<X>.另请参阅Enum.valueOf会针对扩展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(),您将拥有完整的类型信息,而不仅仅是类.
Boh*_*ian 11
键入该genericsHell方法允许它编译:
static <T extends ShapeProcessor<?>> void genericsHell(Class<T> a) {}
编辑:这允许编译器从上下文或通过编码显式类型指定,它ShapeProcessor不是字面上的任何 类型ShapeProcessor,而是与作为参数传递的类型相同的类型.如果调用是显式类型的,(编译器在封面下执行),代码将如下所示:
MyClass.<ShapeSaver>genericsHell(ShapeSaver.class);
有趣的是,它提供了类型警告,但仍然编译.但是,不需要显式类型,因为从参数中可以获得足够的类型信息来推断泛型类型.
你的问题缺少一些声明,所以我添加了它们以创建一个简短的自包含正确的示例 - 即此代码按原样编译
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);
}
| 归档时间: | 
 | 
| 查看次数: | 1668 次 | 
| 最近记录: |