Java 8 不兼容的类型 [英] Java 8 Incompatible Types
问题描述
这是简单的代码
import java.util.ArrayList;导入 java.util.Collections;导入 java.util.HashMap;导入 java.util.Map;公共类 SimpleTest {公共静态无效主(字符串 [] args){最终 ArrayList
当 JAVA_HOME 指向 JDK 8 并且我使用 javac -source 1.7 SimpleTest.java
我得到 p>
SimpleTest.java:9:错误:类型不兼容:ArrayList
当我使用 -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.
,原始类型 Map
将传递给 createMap
,使其成为具有原始结果类型 Map
的未经检查的调用.
所选方法的结果类型确定如下:
如果所选方法声明的返回类型为 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屋!