简体   繁体   English

Java 8不兼容类型

[英]Java 8 Incompatible Types

Here's the simple code 这是简单的代码

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

When JAVA_HOME points to JDK 8 and I use javac -source 1.7 SimpleTest.java I get 当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(
                                                                ^

When I use -source 1.8 or no -source option everything works ok. 当我使用-source 1.8或不使用-source选项时,一切正常。 Now, when JAVA_HOME points to JDK 7 the code always compiles whether I use -source 1.7 or not. 现在,当JAVA_HOME指向JDK 7时,无论是否使用-source 1.7 ,代码总是会编译。

Initially this question was about a piece of software where POM file has a <source> and <target> set to 1.7 and the build was failing on JDK 8 but was ok on JDK 7. 最初,这个问题是关于某个软件的,其中POM文件的<source><target>设置为1.7并且在JDK 8上构建失败,但在JDK 7上可以。

Now for the question - what causes it to happen ? 现在要问的是-导致它发生的原因是什么? It seems to me as a major overlook of some sort. 在我看来,这似乎是一种主要的忽视。 Why compiling on JDK 8 with source set to 1.7 fails ? 为什么在source设置为1.7 JDK 8上编译失败?

You can simplify the code to a self-contained example which doesn't need 3rd-party libraries: 您可以将代码简化为不需要第三方库的独立示例:

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

This can be compiled using javac from jdk1.7, but not with javac from jdk1.8 using -source 1.7 . 这可以使用被编译javac从jdk1.7不是,但javac使用从jdk1.8 -source 1.7

The point here is that javac from jdk1.8 still is a different compiler than the one included in the previous version and the option -source 1.7 doesn't tell it to mimic the old implementation's behavior but to be compatible with the Java 7 specification . 这里的要点是,来自jdk1.8的javac仍然是与先前版本中包含的javac不同的编译器,并且选项-source 1.7不会告诉它模仿旧实现的行为,但要与Java 7 规范兼容。 If the old compiler has a bug, the newer compiler doesn't have to try to reproduce the bug. 如果旧的编译器有错误,则新的编译器不必尝试重现该错误。

Since the code uses Collections.EMPTY_MAP rather than Collections.<String,String>emptyMap() , the raw type Map will be passed to createMap , making it an unchecked invocation having the raw result type Map . 由于代码使用Collections.EMPTY_MAP而不是Collections.<String,String>emptyMap() ,因此原始类型Map将传递给createMap ,使其成为未经检查的具有原始结果类型Map调用。

This is mandated by JLS §15.12.2.6 : 这是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. 如果以返回类型为void声明所选方法,则结果为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. 否则,如果要使该方法适用就必须进行未经检查的转换,则结果类型是该方法的声明的返回类型的擦除(第4.6节)。

...

It seems that this behavior has not been (fully) implemented in javac of jdk1.7. 似乎尚未在jdk1.7的javac中完全实现此行为。 Interestingly, it will do it correctly when adding a type parameter like 有趣的是,添加类型参数时,它将正确执行此操作

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

making it a generic method. 使其成为通用方法。 Then, jdk1.7 will produce the same error. 然后,jdk1.7将产生相同的错误。 But the cited rule applies to all method invocations. 但是引用的规则适用于所有方法调用。


In contrast, the fact that compiling it with Java 8 compliance succeeds stems from the new target type inference, which has entirely different rules. 相反,使用Java 8兼容性进行编译成功的事实源于具有完全不同规则的新目标类型推断。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM