有 Java 编程相关的问题?

你可以在下面搜索框中键入要查询的问题!

Java8不兼容类型

下面是简单的代码

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上没有问题

现在问一个问题——是什么导致了这种情况的发生?在我看来,这是一个重大的疏忽。为什么在JDK 8上source设置为1.7时编译失败


共 (1) 个答案

  1. # 1 楼答案

    您可以将代码简化为一个独立的示例,不需要第三方库:

    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;
        }
    }
    

    这可以使用jdk1中的javac进行编译。7,但不能使用来自jdk1的javac。8使用-source 1.7

    这里的重点是来自jdk1的javac。8仍然是与前一版本中包含的编译器不同的编译器,选项-source 1.7没有要求它模仿旧实现的行为,而是要求它与Java 7规范兼容。如果旧的编译器有一个bug,新的编译器就不必试图重现这个bug

    由于代码使用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.

    这种行为似乎还没有(完全)在jdk1的javac中实现。7.有趣的是,当添加一个类型参数(如

    public static <T> Map<String, Object> createMap(String id, Map<String,String> m)
    

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


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