Вывод общего типа не работает с цепочкой методов?

Это не скомпилируется в 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());
    }
}

Это не означает, что конкретный тип Map возвращается из 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

Он компилируется, если вы меняете вызов f на f(Map.<Integer,String>empty());. В Java 8 он работает, не прибегая к этому.

Но если вы измените вызов f на f(Map.empty().put(1,"A").put(2,"B"));, он не скомпилируется еще раз, как на Java 7, так и на 8. Почему?

$ $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

Ответ 1

Почему?

Поскольку тип вывода типов generics не был расширен до цепочки вызовов.

Из java-учебник по типу типа generics:

Понятие того, что является типом цели, было расширено, чтобы включить аргументы метода.

Вот почему этот код:

f(Map.empty());

компилирует.

Но этот код не потому, что это цепной вызов:

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

Вы также можете найти небольшой абзац в JSR-000335 Лямбда-выражения для JavaTM Final Language для окончательной версии для оценки (в частности, часть D)

Была некоторая заинтересованность в разрешении вывода "chain": in a(). b(), передавая информацию о типе от вызова b к вызову a. Это добавляет еще одно измерение сложности алгоритма вывода, поскольку частичная информация должна проходить в обоих направлениях; он работает только тогда, когда стирание возвращаемого типа a() фиксировано для всех экземпляров (например, List). Эта особенность не очень хорошо вписывается в модель выражения poly, так как тип цели не может быть легко получен; но, возможно, с дополнительными улучшениями, которые могут быть добавлены в будущем.

Так что, возможно, в Java 9.