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

ken*_*ytm 5 java eclipse generics javac

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

package test;

public final class Test {
    public static class N<T extends N<T>> {}

    public static class R<T extends N<T>> {
        public T o;
    }

    public <T extends N<T>> void p(final T n) {}

    public void v(final R<?> r) {
        p(r.o);       // <-- javac fails on this line
    }
}
Run Code Online (Sandbox Code Playgroud)

错误是:

Test.java:13: error: method p in class Test cannot be applied to given types;
        p(r.o);
        ^
  required: T
  found: N<CAP#1>
  reason: inferred type does not conform to declared bound(s)
    inferred: N<CAP#1>
    bound(s): N<N<CAP#1>>
  where T is a type-variable:
    T extends N<T> declared in method <T>p(T)
  where CAP#1 is a fresh type-variable:
    CAP#1 extends N<CAP#1> from capture of ?
1 error

所以问题是:

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

  2. 有没有办法让这个编译javac,而不改变v方法的签名(即保持通配符)?

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

Pau*_*ora 6

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

  1. r.o属于T(声明R),是或扩展N<T>.
  2. 该方法p采用类型T(声明为p)的参数,该参数也是或扩展N<T>.
  3. 因此,即使r打字R<?>,p(r.o)理论上呼叫应该是合法的.

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

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

public void v(final R<?> r) {
    //necessary to placate javac - this is okay because [insert above reasoning]
    @SuppressWarnings("rawtypes")
    N nRaw = r.o;
    p(nRaw);
}
Run Code Online (Sandbox Code Playgroud)