我有这个类,这是我在从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());
}
}
Run Code Online (Sandbox Code Playgroud)
这只是一个非常小的例子来展示问题,实际的代码更有意义.这个问题似乎很普遍,因此是一个抽象的例子.核心问题如下:由于某种原因,javac不希望接受带有类型MyMap<?, ?>作为setMap()方法参数的表达式,即使根据我的理解,这应该是良好类型的.
代码使用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 ?
Run Code Online (Sandbox Code Playgroud)
错误消息似乎表明没有找到第一个类型参数的统一MyMap,javac无法找到与两者统一的类型CAP#1,它表示实际参数中的第一个通配符参数setMap(),以及A相应的类型参数的形式参数setMap().虽然在我看来A并且CAP#1应该是完全可以统一的,但它们都代表了通过擦除签名中的实际类型而引入的存在类型getMap().
谁能发现这里出了什么问题?javac 6错误地接受了这段代码吗?另外,是否有一种不太过于干扰(和javac 6兼容)的方式来指导javac 8走向正确的统一?
编辑:我尝试了从stackoverflow.com/questions/23063474/引入变量的建议,但似乎没有帮助,我得到相同的编译错误.
EDIT2:澄清了示例代码的"意图".
EDIT3:重命名Map为MyMap,显然定义自定义Map类型太混乱了.
可以通过引入中间方法并强制 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());
}
}
Run Code Online (Sandbox Code Playgroud)