Java CRTP 和通配符:代码在 Eclipse 中编译,但不是`javac` [英] Java CRTP and Wildcards: Code compiles in Eclipse but not `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所以问题是:
这是
javac
错误还是 Eclipse 错误?有什么方法可以在
javac
上编译,而不改变v
方法的签名(即保留通配符)?我知道把它改成
确实可以编译它,但我想知道是否有办法先避免这种情况.此外,方法>void v(final R<T> r) p
不能更改为
因为内容的类型需要精确约束>void p(final T n) T extends N
.
通配符的局限性在于它们会破坏类型参数允许的递归表达式,例如 T extends X
.基于以下几点,我们知道您尝试做的事情是安全的:
ro
是T
类型(由R
声明),它是或扩展N
.- 方法
p
接受一个T
类型的参数(由p
声明),它也是或扩展N
.; - 因此,即使
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:
Is this a
javac
bug or Eclipse bug?Is there any way to make this compile on
javac
, without changing the signature of thev
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 methodp
cannot be changed to<T extends N<?>> void p(final T n)
because the content have types which requires the exact constraintT 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:
r.o
is of typeT
(declared byR
), which is or extendsN<T>
.- The method
p
takes an argument of typeT
(declared byp
), which also is or extendsN<T>
. - So even though
r
is typed asR<?>
, a callp(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屋!