在构造函数方法引用中,使用泛型类型参数之间的区别与否?

时间:2018-03-09 18:35:40

标签: java generics lambda method-reference effective-java

我正在阅读Effective Java 3并注意到第43项中的这段代码:“首选方法引用lambdas”:

TreeMap<K,V>::new

注意类型参数。我一直都这么做:

TreeMap::new

我使用Intellij并且从未收到有关此更改或任何更改建议的警告。实际上,当我让IDE将上面的方法引用更改为lambda时,它会将其转换为

() -> new TreeMap<Integer, Integer>()

包含类型参数的价值是多少?编译器不能根据变量的类型参数推断它吗?根据IDE如何将方法引用转换为lambda,它似乎可以。

1 个答案:

答案 0 :(得分:4)

The constructor reference TreeMap::new is the same as using diamond type inference (§15.13.1):

For convenience, when the name of a generic type is used to refer to an instance method (where the receiver becomes the first parameter), the target type is used to determine the type arguments. This facilitates usage like Pair::first in place of Pair<String,Integer>::first.

Similarly, a method reference like Pair::new is treated like a "diamond" instance creation (new Pair<>()). Because the "diamond" is implicit, this form does not instantiate a raw type; in fact, there is no way to express a reference to the constructor of a raw type.

You'd need to provide type arguments explicitly in more or less the same situations as when you would need to provide type arguments to a constructor explicitly.

For example, in the following, the call to get prevents the return assignment from being considered during inference of supplier, so T is inferred to be ArrayList<Object>:

class Example {
    public static void main(String[] args) {
        ArrayList<String> list =
            supplier(ArrayList::new).get(); // compile error
    }
    static <T> Supplier<T> supplier(Supplier<T> s) { return s; }
}

In that contrived example, we'd have to use ArrayList<String>::new.