Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Changes around type inference check in Java 9

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

like image 254
Mozaffar Avatar asked Jan 14 '19 12:01

Mozaffar


People also ask

How does type inference work in Java?

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.

Does Java support type inference?

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.

When did Java introduce inferences?

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.


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.

like image 137
Eugene Avatar answered Oct 06 '22 01:10

Eugene