java 8 个方法引用到 lambda

java 8 method reference into lambda

我有这段代码,我尝试将方法引用 ("String::length") 转换为等效的 lambda 表达式。

 Stream<String> s = Stream.of("hello", "your name", "welcome", "z");

 List<String> list = s.sorted((a, b) -> Comparator.comparingInt(String::length).compare(a, b)).collect(toList());

 // List<String> list = s.sorted((a, b) -> Comparator.comparingInt( p -> {return ((String)p).length();}).compare(a, b)).collect(toList());

注释行中概述了它唯一的工作方式。我需要转换参数 "p"。

似乎在编译时它指定参数的类型 "p" 如果我使用 lambda 表达式并且我需要显式转换为对象。见下文:

<Object> Comparator<Object> java.util.Comparator.comparingInt(ToIntFunction<? super Object> keyExtractor)

当我使用 String::length 方法引用时,隐式参数在编译时被正确理解为 String 实例。这种情况有什么特别之处?见下文。

<String> Comparator<String> java.util.Comparator.comparingInt(ToIntFunction<? super String> keyExtractor)

不要使用实现函数式 Comparator 接口的匿名 lambda,只需使用 straight-up 比较器:

List<String> list = 
    s.sorted(Comparator.comparingInt(String::length)).collect(toList());

EDIT 关于为什么 p 的类型没有被推断出来。

p 的类型不会自动推断为 String,原因与以下示例中未推断出的原因非常相似:

String a = "a";
String b = "b";
Comparator.comparingInt(p -> p.length).compare(a, b);

此处,它失败并显示 Object 没有方法 length 的消息。要理解原因,请考虑这个表达式的抽象语法树(非常粗略的近似):

                                                                  Apply
                                                                 /     \
                                                                /       \
                                                               /         \
                                                              /           \
                                             _______________ /             \__
                                            /                                 \
                                           /                                   \
                                          /                                     \
                                  MemberSelection                             ArgumentList(2)
                                  /             \______                       /             \
                                 /                     \                     /               \
                           Apply                      compare              a: String     b: String
                     _____/     \______
                    /                  \
                   /                    \
          MemberSelection             Lambda
          /          |                 |    \
         /           |                 |     \
Comparator     comparingInt       Variable    MemberSelection
                                       |          /      \
                                       p         p       length

如你所见,关于 String 的类型信息完全在 AST 的右侧部分,而变量 binder p 和整个闭包在 AST 的左侧分支.

恰好类型推断算法总是以 top-down 方式在本地工作。一旦它下降到左子树并且无法推断 p 的类型,它就不会回到树上并在右子树中搜索其他提示。实现起来太复杂了,type-checker 越远离有问题的活页夹 p,有关类型推断失败的错误消息就越不清晰。类型推断算法不会尝试对整个程序进行全局类型检查。


你根本不需要 (a, b) -> ... 部分,Comparator.compare(...) 已经 产生了一个比较器:

List<String> list = s.
  sorted(Comparator.comparingInt(String::length)).
  collect(toList());

做你可能想要的。

String::length 的完整 lambda 是:

(String p) -> p.length()

也可以用block来写,但更常用的是上面更简单的表达式:

(String p) -> { return p.length(); }

如果编译器可以推断类型,您可以省略它:

p -> p.length()

但是你正在使用这个:

Comparator.comparingInt(p -> p.length())

这就是编译器在需要推断 p 类型时所看到的全部内容。那么,什么是p?不知道,编译器说。所以你必须明确指定类型:

// Explicit type in parameter list
Comparator.comparingInt((String p) -> p.length())

// Explicit type on generic type parameter
Comparator.<String>comparingInt(p -> p.length())

// You can do both, but why would you?
Comparator.<String>comparingInt((String s) -> s.length())

// Explicit type of referenced method
Comparator.comparingInt(String::length)

请注意 none 代码使用了强制转换。上面都是type-safe,不像你写的代码用强制转换。不要使用转换!

以上所有 4 个调用 returns a Comparator<String>。该比较器可用于例如对 List<String> 进行排序,但如果您尝试对任何其他类型的列表进行排序,则会出现编译错误。

当你像这样投射时:

Comparator.comparingInt(p -> ((String) p).length())

it returns a Comparator<Object>,这意味着您可以在尝试对任何类型的列表进行排序时给出该比较器,例如一个List<Integer>。它会编译,但在运行时失败。强制转换的使用使得代码不是 type-safe。正如我所说,不要那样做。