所以我们有一个像这样的泛型方法,它是依赖注入初始化的一部分:
public static <TS, TI extends TS> void registerTransient(
Class<TS> serviceClass, Class<TI> implementationClass)
{
//
}在某种程度上,我们发现了一个类可能不一定存在的情况。这是一个实现类,我们将注入多个off (因此服务类与实现类相同)。很自然,你会这样写:
Class<?> clazz = Class.forName("com.acme.components.MyPersonalImplementation");
registerTransient(clazz, clazz);IDEA对此没有问题,但javac抱怨道:
error: method registerTransient in class TestTrash cannot be applied to given types;
required: Class<TS>,Class<TI>
found: Class<CAP#1>,Class<CAP#2>
reason: inferred type does not conform to declared bound(s)
inferred: CAP#2
bound(s): CAP#1
where TS,TI are type-variables:
TS extends Object declared in method <TS,TI>registerTransient(Class<TS>,Class<TI>)
TI extends TS declared in method <TS,TI>registerTransient(Class<TS>,Class<TI>)
where CAP#1,CAP#2 are fresh type-variables:
CAP#1 extends Object from capture of ?
CAP#2 extends Object from capture of ?怎么回事?该方法要求第二个参数是第一个参数的子类。不管?是什么类,它是两个参数的同一个类对象,而且我认为,类总是可以从自身赋值的。这几乎就像是javac不必要地发明了第二个通配符类型用于第二个参数,然后说“哦,天哪,这里有两个通配符,所以我不知道其中一个是不是可以赋值的。”
发布于 2012-01-20 10:15:51
问题是,除非通过显式转换(在捕获转换过程中,它实际上变成了Class<#capture-... of ?> ),否则Class<?>不能转换为任何其他类型。因此,编译器不能(静态地)将这些捕获的类型边界与方法定义的参数化类型相匹配。
尝试将其显式转换为Class first,如下所示:
registerTransient((Class<Object>)clazz, clazz); 通过这种方式,编译器可以将TS绑定到Object,将TI绑定到扩展Object的对象(尽管它仍然会发出警告)。
事实上,IntelliJ的编译器没有对此进行抱怨,这可能是由于某些优化,甚至可能是编译器错误。你应该把它贴出来,然后等待回复。
如果你想用一种稍微不同的方法来检查它,下面的代码仍然不会被编译,即使它“看起来”没问题:
public class A {
static class B {}
static class C extends B {}
static <T, R extends T> void method(final Class<T> t, final Class<R> r) {}
public static final void main(String... args) {
B b = new B();
C c = new C();
Class<?> cb = b.getClass();
Class<?> cc = c.getClass();
method(cb, cc);
}
}在here中看一下。它展示了Java类型系统的非凡视图(尽管相当密集)。
发布于 2013-07-03 11:55:55
您遇到的问题是,根据JLS7 §6.5.6.1 Simple Expression Names,捕获转换针对每个方法参数单独发生
如果表达式名称出现在要进行赋值转换、方法调用转换或强制转换的上下文中,则表达式名称的类型是捕获转换后的字段、局部变量或参数的声明类型(§5.1.10)。
在本例中,“表达式名称”是标识符clazz。正如编译器输出所示,它被捕获了两次,这是JLS所要求的。
处理这个问题的common technique是引入一个帮助器方法,它将通配符绑定到一个类型变量:
private static <T> void registerTransient(Class<T> serviceAndImplClass)
{
registerTransient(serviceAndImplClass, serviceAndImplClass);
}使用通配符调用这个新方法将会起作用:
Class<?> clazz = Class.forName("com.acme.components.MyPersonalImplementation");
registerTransient(clazz);我看到你在评论中提到了这个变通方法。这看起来可能很奇怪,但这实际上是语言设计者想要的方法。
https://stackoverflow.com/questions/8935185
复制相似问题