考虑以下情况:
class C {
void m(Class<?> c1, Class<?> c2) {}
<S, U extends S> void m(S s, U u) {}
}
class x {{
final Class<Integer> cInteger = Integer.class;
final Class<?> cSomething = null;
final C c = new C();
c.m(cInteger, cInteger);
c.m(cSomething, cSomething); // *
}}
使用Oracle的javac,版本1.7.0_01(以及1.7.0,以及OpenJDK的Java 7和Java 6编译器),我在标有// *
的行上收到错误:
错误:对m的引用是不明确的,C中的方法m(Class,Class)和C中的方法m(S,U)匹配
我无法理解为什么会发生这种情况:当参数的静态类型为Class<Integer>
时,编译器能够告诉调用哪个方法,但是Class<?>
存在问题。
IntelliJ的代码分析表明这是可以的,以及JRockit(或Sun的Java 6)编译器。
所以,这里显然存在一个错误,无论是那些说它是对的软件,还是那些说它是错误的软件。
请注意,如果我删除U
的边界(即,如果我将m
声明为<S, U> void M(S s, U u) {}
,它将编译时没有错误。此外,还有一个原始类型的调用(即,Class x = null; m(x, x)
)编译也很好。
那么,根据Java规范,这段代码是有效还是无效?
感谢。
答案 0 :(得分:2)
m(cInteger, cInteger)
失败,第二个m(cSomething, cSomething)
没问题。原始m(x,x)
也无法编译。
(1)m(cInteger,cInteger)
失败
m()匹配参数;对于 m2 ,推理产生S=U=Class<Integer>
。
现在的问题是, m1 比 m2 更具体吗?遵循15.12.2.5中的程序,它是。如果是这样的话,就没有歧义;应该选择 m1 作为最具体的方法,并且应该编译调用。
但是,这违反了规范给出的非正式要求:如果 m1 比 m2 更具体,则由 m1 可以传递给 m2 而不会出现编译时类型错误。例如, m1 可以接受参数(Class<Integer>,Class<String>)
,其中 m2 不能(由于不完美的类型推理)
过程)。
Javac显然坚持非正式观念;这可能是因为正式规范有一个错误 - 它应该在更具体的关系的定义中包含捕获转换(稍后解释);那么 m1 并不比 m2 更具体,因此调用m(cInteger,cInteger)
是不明确的。
如果另一个编译器严格遵守正式规范,继承规范的错误并不是它的错误。
(2)m(x, x)
失败
与(1)相同的原因;两种方法都匹配,但两者都没有比另一种更具体。
m1 通过方法调用转换进行匹配,这允许未经检查的转化从原始Class
到Class<?>
。在推断S=U=Class
(3)m(cSomething, cSomething)
编译
这是因为 m1 是唯一适用的,因此没有歧义。
m2 不适用,让我们看看为什么。
首先,参数的类型并不完全(Class<?>,Class<?>)
- 捕获转换首先应用于它们。 (同样,规范还不清楚(见第15章),但我非常确定这是很好理解的情况;任何表达式的类型都应用于捕获转换)
所以参数类型是(Class<X1>,Class<X2>)
,有两个新的类型变量。这里有另一个搞砸了,更精确的转换是(Class<X1>,Class<X1>)
,不幸的是,捕获转换应用了两次,独立地,两种不同的类型。
m1 可轻松匹配参数类型。但由于不完美的类型推断过程, m2 不匹配。该过程首先产生S=Class<X1>, U=Class<X2>
,,然后检查,类型变量的边界,U extends S
失败。
(4)删除U的绑定
现在(1),(2),(3)全部编译。因为没有U extends S
,推理就会通过。
对于(1)和(2), m1 现在比 m2 更具体,不再含糊不清。
对于(3), m2 现在匹配;但后来被更具体的 m1
所遮蔽答案 1 :(得分:0)
也许是因为如果您在方法调用中说<S>
类型为Class<?>
,那么<U>
也是Class<?>
,并且无法确定应该使用哪种方法。< / p>
<S, U extends S> void m(S s, U u) {} //S=U=Class<?> => void m(Class<?> s, Class<?> u) {} //same signature
如果您说<S>
和<U>
类型为Class<Integer>
,那么它必须是第二种使用泛型<S>
和<U>
的方法。
<S, U extend S> void m(S s, U u) {} //S=U=Class<Integer> => void m(Class<Integer> s, Class<Integer> u) {} //Ok
问题
<S,U>
声明指定您打算使用两种不同的不相关类型。