Java CRTP和通配符:代码在Eclipse中编译,但不是`javac`

对不起,标题含糊不清。 我有这段代码编译Eclipse Juno(4.2)但不是javac(1.7.0_09):

package test; public final class Test { public static class N<T extends N> {} public static class R<T extends N> { public T o; } public <T extends N> void p(final T n) {} public void v(final R r) { p(ro); // <-- javac fails on this line } } 

错误是:

 Test.java:13:错误:类Test中的方法p不能应用于给定的类型;
         P(ω);
         ^
  要求:T
  发现:N 
   reason:推断类型不符合声明的边界
    推断:N 
     bound(s):N <N >
  其中T是一个类型变量:
     T扩展方法 p(T)中声明的N 
  其中CAP#1是一个新的类型变量:
     CAP#1扩展N 从捕获?
 1错误

所以问题是:

  1. 这是一个javac bug还是Eclipse bug?

  2. 有没有办法在javac上进行javac ,而不更改v方法的签名(即保留通配符)?

    我知道将它更改为<T extends N> void v(final R r)确实使它编译,但我想知道是否有办法首先避免这种情况。 此外,方法p不能改变为<T extends N> void p(final T n)因为内容具有需要精确约束T extends N

通配符的局限性在于它们会破坏类型参数允许的T extends X类的递归表达式。 根据以下内容,我们知道您要做的是安全的:

  1. roT型(由R声明),它是或者延伸N
  2. 方法p采用类型为T的参数(由p声明),它也是或者扩展N
  3. 因此即使r被输入为R ,呼叫p(ro)理论上应该是合法的。

这可能是eclipse编译器的推理(已知对javac没有的generics的某些细微差别做出正确的限制)。

假设您想使用javac编译并且不能像您提到的那样更改v的签名,那么您可以做的最好就是使用原始类型,它“选择”generics类型检查:

 public void v(final R r) { //necessary to placate javac - this is okay because [insert above reasoning] @SuppressWarnings("rawtypes") N nRaw = ro; p(nRaw); }