Java 8 不兼容的类型

标签 java java-8 java-7 compatibility javac

这是简单的代码

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/

相关文章:

java - 如何使用MockClassLoader?

java - JSR082 - LocalDevice.getLocalDevice() 返回 null

java - 如何获取特定的类对象

datetime - 为什么输出中出现负号?

java - 使用 Java 8 过滤结果集的多个条件

java - Maven包编译错误

java - 将图表保存在 Excel 工作表中

java - Java 8 中的流 : simple solution to use parallelstream() on a cluster?

file - 如何在 Files.walkFileTree 期间防止 AccessDeniedException?

tomcat - 不能允许 Tomcat6 中的 Java 编码名称