对于模糊的标题,我们深表歉意。我有这段代码编译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
所以问题是:
这是一个javac
错误还是Eclipse错误?
有没有办法在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>
。
答案 0 :(得分:6)
通配符的局限在于它们会破坏类型参数允许的递归表达式,如T extends X<T>
。根据以下内容,我们知道您尝试做的事情是安全的:
r.o
属于T
类型(由R
声明),其为N<T>
或扩展为p
。T
采用p
类型的参数(由N<T>
声明),该参数也是或r
。R<?>
被输入p(r.o)
,理论上呼叫v
也应合法。这可能是日食编译器的推理(已知可以对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}}