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> 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 createMap(String value1, String value2, Map object1, Map object2) {
Map map = new HashMap<>();
map.put("value1", value1);
map.put("value1", value1);
map.put("object1", object1);
map.put("object2", object2);
return map;
}
public static ArrayList newArrayList(E... elements) {
ArrayList list = new ArrayList(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 cannot be converted to ArrayList>
final ArrayList> 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 and 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 ArrayList newArrayList(T... arg) {
return new ArrayList(Arrays.asList(arg));
}
private final List> maps = newArrayList(
createMap(null, Collections.EMPTY_MAP)
);
public static Map createMap(String id, Map 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.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 Map createMap(String id, Map 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.