我有这个类,这是我在从Java 6移植到Java 8的项目中找到的一些代码的简化:
public class Unification {
final class Box<A> {}
final class MyMap<A, B extends Box<? extends A>> {}
MyMap<?, ?> getMap() {
return new MyMap<Object, Box<Object>>();
}
<A, B extends Box<? extends A>> void setMap(final MyMap<A, B> m) {}
void compileError() {
setMap(getMap());
}
}
仅仅展示问题是一个非常小的例子,实际的代码更有意义。这个问题似乎很普遍,因此是一个抽象的例子。核心问题如下:由于某种原因,javac不希望接受类型为MyMap<?, ?>
的表达式作为setMap()
方法的参数,即使根据我的理解,这应该是好的-typed。
代码使用javac 6编译时没有错误,但是当我使用javac 8时,我得到了这个不起眼的错误消息:
C:\System9\KWS_sparse\sourcesNG\Domain\src\uz\Unification.java (21:9) error: method setMap in class Unification cannot be applied to given types;
required: Unification.MyMap<A,B>
found: Unification.MyMap<CAP#1,CAP#2>
reason: inference variable A has incompatible bounds
equality constraints: CAP#1
lower bounds: Object
where A,B are type-variables:
A extends Object declared in method <A,B>setMap(Unification.MyMap<A,B>)
B extends Unification.Box<? extends A> declared in method <A,B>setMap(Unification.MyMap<A,B>)
where CAP#1,CAP#2 are fresh type-variables:
CAP#1 extends Object from capture of ?
CAP#2 extends Unification.Box<? extends CAP#1> from capture of ?
错误消息似乎表明找不到MyMap
的第一个类型参数的统一,javac无法找到与CAP#1
统一的类型,setMap()
表示第一个通配符参数实际参数A
和setMap()
,它是A
形式参数的相应类型参数。虽然在我看来CAP#1
和getMap()
应该是完全一致的,但它们都代表了通过删除Map
签名中的实际类型而引入的存在类型。
有人能发现这里出了什么问题吗? javac 6错误地接受了这段代码吗?另外,是否有一种不太过于干扰(和javac 6兼容)的方式来指导javac 8走向正确的统一?
编辑:我尝试了从stackoverflow.com/questions/23063474/引入变量的建议,但这似乎没有帮助,我得到相同的编译错误。EDIT2:澄清了示例代码的“意图”。
EDIT3:将MyMap
重命名为Map
,显然定义自定义Route::resource creates multiple routes to handle a variety of RESTful actions on the resource
类型太麻烦了。
答案 0 :(得分:0)
通过引入中间方法可以避免这个问题,从而迫使javac分两个阶段进行类型推断。第一种方法只接受一个类型参数,并将第二个作为通配符。 Javac能够正确地推断出此方法的单一类型参数。然后,第二个原始方法能够从提供的参数中正确地推断出第二个类型参数。
public class Unification {
final class Box<A> {}
final class MyMap<A, B extends Box<? extends A>> {}
MyMap<?, ?> getMap() {
return new MyMap<Object, Box<Object>>();
}
<A> void setMap(final MyMap<A, ?> m) {
doSetMap(m);
}
<A, B extends Box<? extends A>> void doSetMap(final MyMap<A, B> m) {}
void worksFineNow() {
setMap(getMap());
}
}