这是简单的代码
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
public class SimpleTest {
public static void main(String[] args) {
final ArrayList<Map<String, Object>> maps = newArrayList(
createMap("1", "a", Collections.EMPTY_MAP, Collections.EMPTY_MAP),
createMap("2", "b", Collections.EMPTY_MAP, Collections.EMPTY_MAP),
createMap("3", "c", Collections.EMPTY_MAP, Collections.EMPTY_MAP)
);
System.out.println(" maps = " + maps);
}
public static Map<String, Object> createMap(String value1, String value2, Map<String, Object> object1, Map<String, Object> object2) {
Map<String, Object> map = new HashMap<>();
map.put("value1", value1);
map.put("value1", value1);
map.put("object1", object1);
map.put("object2", object2);
return map;
}
public static <E> ArrayList<E> newArrayList(E... elements) {
ArrayList<E> list = new ArrayList<E>(elements.length);
Collections.addAll(list, elements);
return list;
}
}
当 JAVA_HOME 指向 JDK 8 并且我使用 javac -source 1.7 SimpleTest.java
我明白了
SimpleTest.java:9: error: incompatible types: ArrayList<Map> cannot be converted to ArrayList<Map<String,Object>>
final ArrayList<Map<String, Object>> maps = newArrayList(
^
当我使用 -source 1.8
时或者没有 -source
选项一切正常。现在,当 JAVA_HOME 指向 JDK 7 时,无论我是否使用 -source 1.7
,代码总是会编译。或不。
最初这个问题是关于一个软件,其中 POM 文件有一个 <source>
和 <target>
设置为 1.7
构建在 JDK 8 上失败,但在 JDK 7 上没问题。
现在的问题 - 是什么导致了它的发生?在我看来,这是某种重大的忽视。为什么用 source
在 JDK 8 上编译设置为 1.7
失败?
最佳答案
您可以将代码简化为不需要第 3 方库的独立示例:
public class Test2 {
@SafeVarargs
static <T> ArrayList<T> newArrayList(T... arg) {
return new ArrayList<T>(Arrays.asList(arg));
}
private final List<Map<String, Object>> maps = newArrayList(
createMap(null, Collections.EMPTY_MAP)
);
public static Map<String, Object> createMap(String id, Map<String,String> m) {
return null;
}
}
这可以使用 javac
编译来自 jdk1.7,但不适用于 javac
从 jdk1.8 使用 -source 1.7
.
这里的重点是 javac
来自 jdk1.8 的编译器仍然与以前版本中包含的编译器和选项 -source 1.7
不同。不会告诉它模仿旧实现的行为,而是要与 Java 7 规范兼容。如果旧编译器有错误,新编译器不必尝试重现错误。
由于代码使用了 Collections.EMPTY_MAP
而不是 Collections.<String,String>emptyMap()
, 原始类型 Map
将传递给 createMap
,使其成为具有原始结果类型 Map
的未经检查的调用.
这是由 JLS §15.12.2.6 强制执行的:
The result type of the chosen method is determined as follows:
If the chosen method is declared with a return type of void, then the result is void.
Otherwise, if unchecked conversion was necessary for the method to be applicable, then the result type is the erasure (§4.6) of the method's declared return type.
…
似乎此行为尚未(完全)在 javac
中实现jdk1.7的。有趣的是,它会在添加类型参数时正确执行,例如
public static <T> Map<String, Object> createMap(String id, Map<String,String> m)
使其成为通用方法。然后,jdk1.7会产生同样的错误。但引用的规则适用于所有 方法调用。
相比之下,使用 Java 8 合规性成功编译它的事实源于新的目标类型推断,它具有完全不同的规则。
关于Java 8 不兼容的类型,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/34659055/