Java 8 个不兼容的类型
Java 8 Incompatible Types
这是简单的代码
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
失败?
您可以将代码简化为不需要第 3 方库的 self-contained 示例:
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.7 中的 javac
进行编译,但不能使用 jdk1.8 中的 javac
使用 -source 1.7
.
这里的要点是来自 jdk1.8 的 javac
仍然是一个与以前版本中包含的编译器不同的编译器,选项 -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.
…
似乎在 jdk1.7 的 javac
中还没有(完全)实现此行为。有趣的是,它会在添加类型参数时正确执行
public static <T> Map<String, Object> createMap(String id, Map<String,String> m)
使其成为通用方法。然后,jdk1.7会产生同样的错误。但是引用的规则适用于 all 方法调用。
相比之下,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
失败?
您可以将代码简化为不需要第 3 方库的 self-contained 示例:
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.7 中的 javac
进行编译,但不能使用 jdk1.8 中的 javac
使用 -source 1.7
.
这里的要点是来自 jdk1.8 的 javac
仍然是一个与以前版本中包含的编译器不同的编译器,选项 -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.
…
似乎在 jdk1.7 的 javac
中还没有(完全)实现此行为。有趣的是,它会在添加类型参数时正确执行
public static <T> Map<String, Object> createMap(String id, Map<String,String> m)
使其成为通用方法。然后,jdk1.7会产生同样的错误。但是引用的规则适用于 all 方法调用。
相比之下,Java8合规编译成功的事实源于新的目标类型推断,它具有完全不同的规则。