Changes around type inference check in Java 9

Viewed 186

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

1 Answers

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.

Related