Java CRTP和通配符:代码在Eclipse中编译而不是“javac” [英] Java CRTP and Wildcards: Code compiles in Eclipse but not `javac`
问题描述
包测试;
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在此行上失败
}
}
错误是:
Test.java:13:错误:类Test中的方法p不能应用于给定类型;
p(r.o);
^
需要:T
找到:N< CAP#1>
原因:推断类型不符合声明的绑定
推断:N
bound(s):N>
其中T是一个类型变量:
T扩展在方法< T>中声明的N < p(T)
其中CAP#1是新鲜的类型变量:
CAP#1从N的捕获中扩展N
1错误
所以问题是:
-
这是一个
javac
错误或Eclipse错误? -
是否在
v
方法的签名(即保留通配符)的情况下,以任何方式在javac
中进行编译?
我知道将其更改为
< T扩展N< T> void v(final R T r)
确实使其编译,但是我想知道是否有办法避免这种情况。此外,方法p
不能更改为< T extends N<?>> void p(final T n)
因为内容具有需要确切约束的类型T扩展N
通配符有限,因为它们打破递归表达式,如 T扩展X< ; T>
类型参数允许。我们知道您要做的是基于以下方面的安全:-
ro
是T
(由R
声明),它是或扩展N <
。 - 方法
p
采用类型为T
(由p
声明),它也是或扩展N
li>
- 所以即使
r
键入为R<?>
理论上来说,p(ro)
- 所以即使
这可能是推理的eclipse编译器(已知对于某些特定细微差别, javac不)进行正确的配额。
假设您要使用javac进行编译,并且无法像您所提到的那样更改
v
的签名,所以您最好的做法是诉诸使用原始类型,选择退出通用类型检查:publ ic void v(最终R' r){
//必须安装javac - 这是可以的,因为[insert above reasoning]
@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屋!
-