Java CRTP 和通配符:代码在 Eclipse 中编译,但不是`javac` [英] Java CRTP and Wildcards: Code compiles in Eclipse but not `javac`

查看:33
本文介绍了Java CRTP 和通配符:代码在 Eclipse 中编译,但不是`javac`的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

抱歉标题含糊不清.我有一段在 Eclipse Juno (4.2) 但不是 javac (1.7.0_09) 上编译的代码:

包测试;公共最终类测试{公共静态类 N<T 扩展 N<T>{}公共静态类 R<T 扩展 N<T>{公众号;}public <T扩展N<T>void p(final T n) {}公共无效 v(最终 R ? r) {p(r.o);//<-- javac 在这一行失败}}

错误是:

<前>Test.java:13: 错误:Test 类中的方法 p 不能应用于给定类型;p(r.o);^要求:T发现:N原因:推断的类型不符合声明的边界推断:N边界:N>其中 T 是类型变量:T 扩展 N 在方法 p(T) 中声明其中 CAP#1 是一个新的类型变量:CAP#1 将 N<CAP#1> 从 ?1 错误

所以问题是:

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

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

    我知道把它改成 >void v(final R<T> r) 确实可以编译它,但我想知道是否有办法先避免这种情况.此外,方法 p 不能更改为 >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 没有).

假设你想用 javac 编译并且不能像你提到的那样改变 v 的签名,你能做的最好的事情就是使用原始类型,它选择退出"泛型类型检查:

public void v(final R r) {//有必要安抚javac - 这是可以的,因为[插入上面的推理]@SuppressWarnings("rawtypes")N nRaw = r.o;p(nRaw);}

Sorry for the vague title. I have this piece of code which compiles on Eclipse Juno (4.2) but not 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
    }
}

The error is:

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

So the questions are:

  1. Is this a javac bug or Eclipse bug?

  2. Is there any way to make this compile on javac, without changing the signature of the v method (i.e. keep the wildcard)?

    I know changing it to <T extends N<T>> void v(final R<T> r) does make it compile, but I would like to know if there's way to avoid this first. Also, the method p cannot be changed to <T extends N<?>> void p(final T n) because the content have types which requires the exact constraint T extends N<T>.

解决方案

Wildcards are limited in that they break recursive expressions like T extends X<T> that type parameters allow. We know what you're trying to do is safe based on the following:

  1. r.o is of type T (declared by R), which is or extends N<T>.
  2. The method p takes an argument of type T (declared by p), which also is or extends N<T>.
  3. So even though r is typed as R<?>, a call p(r.o) should theoretically be legal.

This is possibly the reasoning of the eclipse compiler (known to make correct allowances for certain nuances of generics where javac doesn't).

Assuming you want to compile with javac and can't change the signature of v like you mentioned, the best you can do is resort to using a raw type, which "opts out" of generic type checking:

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);
}

这篇关于Java CRTP 和通配符:代码在 Eclipse 中编译,但不是`javac`的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆