Java 8不兼容的类型

这是简单的代码

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> 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 createMap(String value1, String value2, Map object1, Map object2) { Map map = new HashMap(); map.put("value1", value1); map.put("value1", value1); map.put("object1", object1); map.put("object2", object2); return map; } public static  ArrayList newArrayList(E... elements) { ArrayList list = new ArrayList(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 cannot be converted to ArrayList<Map> final ArrayList<Map> maps = newArrayList( ^ 

当我使用-source 1.8或no -source选项时,一切正常。 现在,当JAVA_HOME指向JDK 7时,代码总是编译我是否使用-source 1.7

最初这个问题是关于一个软件,其中POM文件的设置为1.7 ,并且构建在JDK 8上失败但在JDK 7上没问题。

现在提出问题 – 是什么导致它发生的? 在我看来,这是某种主要的忽视。 为什么在source设置为1.7 JDK 8上进行编译失败?

您可以将代码简化为不需要第三方库的自包含示例:

 public class Test2 { @SafeVarargs static  ArrayList newArrayList(T... arg) { return new ArrayList(Arrays.asList(arg)); } private final List> maps = newArrayList( createMap(null, Collections.EMPTY_MAP) ); public static Map createMap(String id, Map m) { return null; } } 

这可以使用jdk1.7中的javac编译,但不能使用-source 1.7使用jdk1.8中的javac

这里的要点是jdk1.8中的javac仍然是与前一版本中包含的编译器不同的编译器,而-source 1.7选项并不告诉它模仿旧实现的行为,而是与Java 7 规范兼容。 如果旧编译器有错误,则较新的编译器不必尝试重现该错误。

由于代码使用Collections.EMPTY_MAP而不是Collections.emptyMap() ,原始类型Map将传递给createMap ,使其成为具有原始结果类型Map的未经检查的调用。

这是JLS§15.12.2.6规定的:

所选方法的结果类型确定如下:

  • 如果声明所选方法的返回类型为void,则结果为void。

  • 否则,如果该方法需要未经检查的转换,则结果类型是方法声明的返回类型的擦除(第4.6节)。

似乎这种行为还没有(完全)在jdk1.7的javac中实现。 有趣的是,它会在添加类型参数时正确执行

 public static  Map createMap(String id, Map m) 

使其成为通用方法。 然后,jdk1.7将产生相同的错误。 但引用的规则适用于所有方法调用。


相比之下,使用Java 8合规性编译它的事实成功源于新的目标类型推断,它具有完全不同的规则。