Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Generic type inference not working with method chaining?

This code compiles in Java 8 but fails to compile in Java 7:

class Map<K,V> {     static <K,V> Map<K,V> empty() {return null;}     Map<K,V> put(K k, V v) {return null;}     V get(K k) {return null;} }  class A {     static void f(Map<Integer,String> m){}     public static void main(String[] args) {         f(Map.empty());     } } 

It doesn't infer the concrete type of the Map being returned from Map.empty():

$ javac7 A.java A.java:10: error: method f in class A cannot be applied to given types;         f(Map.empty());         ^   required: Map<Integer,String>   found: Map<Object,Object>   reason: actual argument Map<Object,Object> cannot be converted to Map<Integer,String> by method invocation conversion 1 error 

It compiles if you change the f call to f(Map.<Integer,String>empty());. In Java 8, it works without having to resort to this.

But if you change the f call to f(Map.empty().put(1,"A").put(2,"B"));, it fails to compile once again, on both Java 7 and 8. Why?

$ $javac7 A.java  A.java:10: error: method f in class A cannot be applied to given types;         f(Map.empty().put(1,"A").put(2,"B"));         ^   required: Map<Integer,String>   found: Map<Object,Object>   reason: actual argument Map<Object,Object> cannot be converted to Map<Integer,String> by method invocation conversion 1 error  $ $javac8 A.java A.java:10: error: incompatible types: Map<Object,Object> cannot be converted to Map<Integer,String>         f(Map.empty().put(1,"A").put(2,"B"));                                     ^ Note: Some messages have been simplified; recompile with -Xdiags:verbose to get full output 1 error  $ $javac8 -Xdiags:verbose A.java A.java:10: error: method f in class A cannot be applied to given types;         f(Map.empty().put(1,"A").put(2,"B"));         ^   required: Map<Integer,String>   found: Map<Object,Object>   reason: argument mismatch; Map<Object,Object> cannot be converted to Map<Integer,String> 1 error 
like image 785
Dog Avatar asked Jul 17 '14 04:07

Dog


2 Answers

Why ?

Because the type inference of generics types has not been expanded to chained invocation.

From the java tutorial on generics type inference:

The notion of what is a target type has been expanded to include method arguments.

That is why this code:

f(Map.empty()); 

compiles.

But this code doesn't because this is a chained invocation:

f(Map.empty().put(1,"A").put(2,"B")); 

You can also find a small paragraph in the JSR-000335 Lambda Expressions for the JavaTM Programming Language Final Release for Evaluation (specifically part D):

There has been some interest in allowing inference to "chain": in a().b(), passing type information from the invocation of b to the invocation of a. This adds another dimension to the complexity of the inference algorithm, as partial information has to pass in both directions; it only works when the erasure of the return type of a() is fixed for all instantiations (e.g. List). This feature would not fit very well into the poly expression model, since the target type cannot be easily derived; but perhaps with additional enhancements it could be added in the future.

So maybe in Java 9.

like image 132
gontard Avatar answered Oct 20 '22 01:10

gontard


What is the type of Map.Entry.comparingByValue().reversed()? - Just to answer this question simpler.

unSortedMap.entrySet().stream()                     .filter(e -> e.getValue() > 1)                     .sorted(Entry.comparingByValue(Comparator.reverseOrder()))                     .collect(Collectors.toMap(Entry::getKey, Entry::getValue,                             (e1, e2) -> e1, LinkedHashMap::new));
like image 35
Ram Govind Avatar answered Oct 20 '22 01:10

Ram Govind