通用类型推断无法使用方法链接?

Dog*_*Dog 27 java generics type-inference java-7 java-8

此代码在Java 8中编译,但无法在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());
    }
}
Run Code Online (Sandbox Code Playgroud)

它不会推断出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
Run Code Online (Sandbox Code Playgroud)

如果您将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
Run Code Online (Sandbox Code Playgroud)

gon*_*ard 32

为什么?

因为泛型类型的类型推断尚未扩展为链式调用.

泛型类型推断java教程:

什么是目标类型的概念已经扩展到包括方法参数.

这就是为什么这段代码:

f(Map.empty());
Run Code Online (Sandbox Code Playgroud)

编译.

但是这段代码没有,因为这是一个链式调用:

f(Map.empty().put(1,"A").put(2,"B"));
Run Code Online (Sandbox Code Playgroud)

您还可以在JSR-000335 Lambda表达式中找到JavaTM编程语言评估最终版本的小段落(特别是D部分):

允许推理"链"有一些兴趣:在a().b()中,将类型信息从b的调用传递给a的调用.这为推理算法的复杂性增加了另一个维度,因为部分信息必须在两个方向上传递; 它只适用于a()的返回类型的擦除对于所有实例化(例如List)是固定的.此特征不适合多聚表达模型,因为目标类型不能轻易导出; 但也许还有其他增强功能,可以在将来添加.

也许在Java 9中.