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

时间:2012-11-03 17:45:06

标签: 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
    }
}

错误是:

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. 这是一个javac错误还是Eclipse错误?

  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>

1 个答案:

答案 0 :(得分:6)

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

  1. r.o属于T类型(由R声明),其为N<T>或扩展为p
  2. 方法T采用p类型的参数(由N<T>声明),该参数也是或r
  3. 因此即使R<?>被输入p(r.o),理论上呼叫v也应合法。
  4. 这可能是日食编译器的推理(已知可以对javac doesn't)中的某些细微差别进行正确的限制。

    假设您想使用javac进行编译并且不能像您提到的那样更改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); } 的签名,那么您可以做的最好就是使用原始类型,其中&#34;选择退出&#34; 34;通用类型检查:

    {{1}}