Java 8 不兼容的类型 [英] Java 8 Incompatible Types

查看:53
本文介绍了Java 8 不兼容的类型的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

这是简单的代码

import java.util.ArrayList;导入 java.util.Collections;导入 java.util.HashMap;导入 java.util.Map;公共类 SimpleTest {公共静态无效主(字符串 [] args){最终 ArrayList>地图 = 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 MapcreateMap(String value1, String value2, Map object1, Map object2) {映射<字符串,对象>map = new HashMap<>();map.put("value1", value1);map.put("value1", value1);map.put("object1", object1);map.put("object2", object2);返回地图;}公共静态<E>ArrayListnewArrayList(E... 元素) {ArrayListlist = new ArrayList(elements.length);Collections.addAll(list, 元素);退货清单;}}

当 JAVA_HOME 指向 JDK 8 并且我使用 javac -source 1.7 SimpleTest.java 我得到

SimpleTest.java:9:错误:类型不兼容:ArrayList无法转换为 ArrayList>最终 ArrayList>地图 = newArrayList(^

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

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

现在的问题 - 是什么导致它发生?在我看来,这似乎是某种主要的忽视.为什么在 source 设置为 1.7 的 JDK 8 上编译失败?

解决方案

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

公共类Test2 {@SafeVarargs静态 <T>ArrayListnewArrayList(T ... arg) {返回新的 ArrayList(Arrays.asList(arg));}private final List>地图 = newArrayList(createMap(null, Collections.EMPTY_MAP));public static MapcreateMap(String id, Map m) {返回空;}}

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

这里的重点是 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 映射<字符串,对象>createMap(String id, Map m)

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


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

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

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. Now, when JAVA_HOME points to JDK 7 the code always compiles whether I use -source 1.7 or not.

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.

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 ?

解决方案

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.

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. 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.

This is mandated by 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.

It seems that this behavior has not been (fully) implemented in javac of jdk1.7. 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. 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 不兼容的类型的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆