Code (Shortened the actual code to explain thew question).
import java.util.Map;
import java.util.HashMap;
public class TypeReferenceTest {
public static class Model {
public void setAbc(Abc<String> abc) { }
}
public static class Abc<T> {
public Abc(T val) { }
}
public static void main(String[] args) {
Map<String, Object> attrMap = new HashMap<>();
attrMap.put("key", 0);
Model m = new Model ();
m.setAbc(new Abc<>(getAttrOrDefault(attrMap, "key", "Default")));
System.out.println("Test completed.....");
}
public static <T extends Object> T getAttrOrDefault(Map<String, Object> attrMap, String attrName, T defaultValue) {
@SuppressWarnings("unchecked")
T attrValue = (T)attrMap.get(attrName);
return (attrValue == null) ? defaultValue : attrValue;
}
}
Test
host:~/temp/test> /usr/local/java/jdk1.8/bin/javac TypeReferenceTest.java
host:~/temp/test> file TypeReferenceTest.class
TypeReferenceTest.class: compiled Java class data, version 52.0 (Java 1.8)
host:~/temp/test> /usr/local/java/jdk9/bin/java TypeReferenceTest
Test completed.....
host:~/temp/test> /usr/local/java/jdk9/bin/javac TypeReferenceTest.java
host:~/temp/test> file TypeReferenceTest.class
TypeReferenceTest.class: compiled Java class data, version 53.0
host:~/temp/test> /usr/local/java/jdk9/bin/java TypeReferenceTest
Exception in thread "main" java.lang.ClassCastException: java.base/java.lang.Integer cannot be cast to java.base/java.lang.String
at TypeReferenceTest.main(TypeReferenceTest.java:18)
host:~/temp/test>
Please note the exception when the same code was run on Java 9 compiled code. I understand the the reason why code caused ClassCastException, but it is OK if code is compiled with Java 8 (runtime being Java 9 in both the cases). To see the difference, I used javap and disassembled the code to see the diff.
Java 8 compiled disassembled code (only section of interest here)
39: invokestatic #11 // Method getAttrOrDefault:(Ljava/util/Map;Ljava/lang/String;Ljava/lang/Object;)Ljava/lang/Object;
42: invokespecial
Java 9 compiled disassembled code (only section of interest here)
39: invokestatic #11 // Method getAttrOrDefault:(Ljava/util/Map;Ljava/lang/String;Ljava/lang/Object;)Ljava/lang/Object;
42: checkcast #12 // class java/lang/String
45: invokespecial
Noting the difference in Java 9 compiled disassembled code, it is explicitly checking the type in relevant instruction. Of course, that should have been easily inferred from the code that return type should be string but there was no explicit check earlier.
Questions: Has there been some change around type inference in Java 9 and adding explicit check? If yes, where can I find the details (could not find in changelog)? Is it some compilation default option that has been changed in Java 9 that is adding this explicit type check in java 9?
Thanks, Mozaffar
Type inference represents the Java compiler's ability to look at a method invocation and its corresponding declaration to check and determine the type argument(s). The inference algorithm checks the types of the arguments and, if available, assigned type is returned.
Type inference is a Java compiler's ability to look at each method invocation and corresponding declaration to determine the type argument (or arguments) that make the invocation applicable.
1. Introduction. Type Inference was introduced in Java 5 to complement the introduction of generics and was substantially expanded in following Java releases, which is also referred to as Generalized Target-Type Inference. In this tutorial, we'll explore this concept with code samples.
It looks like this was a bug in java-8, most probably this one. And it got fixed in java-9. The checkcast
must be there to begin with since you only take Abc<String>
as input, in my opinion.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With