【问题标题】:Java CRTP and Wildcards: Code compiles in Eclipse but not `javac`Java CRTP 和通配符:代码在 Eclipse 中编译,但不是 `javac`
【发布时间】:2012-11-03 17:45:05
【问题描述】:

抱歉标题含糊。我有这段代码可以在 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:错误:Test 类中的方法 p 不能应用于给定类型; p(r.o); ^ 要求:T 找到:N 原因:推断类型不符合声明的边界 推断:N 界限:N> 其中 T 是一个类型变量: T 扩展了在方法 p(T) 中声明的 N 其中 CAP#1 是一个新的类型变量: CAP#1 扩展了 N 从捕获的 ? 1 个错误

所以问题是:

  1. 这是 javac 错误还是 Eclipse 错误?

  2. 有什么方法可以在javac 上编译,而不更改v 方法的签名(即保留通配符)?

    我知道将其更改为 &lt;T extends N&lt;T&gt;&gt; void v(final R&lt;T&gt; r) 确实可以编译,但我想知道是否有办法首先避免这种情况。此外,方法p 不能更改为&lt;T extends N&lt;?&gt;&gt; void p(final T n),因为内容具有需要精确约束T extends N&lt;T&gt; 的类型。

【问题讨论】:

  • @Nambari:也是1.7。另外,Eclipse 不使用javac 编译代码,而是使用自己的编译器。
  • 它在我的 eclipse 中用 java 6 编译,你用的是什么 java 版本?
  • @AmitD:是的,它可以在 Eclipse 1.6 和 1.7 上运行,但它不能与 OpenJDK 6 和 7 的 javac 编译,也不能使用 Sun's JDK 6 on ideone
  • 我认为没有办法使用 javac 进行编译,除非您按照建议进行更改。

标签: java eclipse generics javac


【解决方案1】:

通配符的局限性在于它们会破坏类型参数允许的递归表达式,例如 T extends X&lt;T&gt;。基于以下几点,我们知道您尝试做的事情是安全的:

  1. r.oT 类型(由 R 声明),是 N&lt;T&gt; 或扩展 N&lt;T&gt;
  2. 方法p 采用T 类型的参数(由p 声明),它也是N&lt;T&gt; 或扩展N&lt;T&gt;
  3. 因此,即使r 被键入为R&lt;?&gt;,调用p(r.o) 理论上应该是合法的。

这可能是 eclipse 编译器的原因(已知它会正确考虑泛型的某些细微差别,其中 javac doesn't)。

假设你想用 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);
}

【讨论】:

    猜你喜欢
    • 2012-11-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-02-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多