java 8方法引用到lambda [duplicate]

问题描述 投票:1回答:3

我有这个代码,我尝试将方法引用(“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”。

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

<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)
java sorting compiler-errors java-8 comparator
3个回答
2
投票

String::length的完整lambda是:

(String p) -> p.length()

它也可以使用块编写,但使用上面更简单的表达式更为常见:

(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)

请注意,没有任何代码使用强制转换。上面的内容都是类型安全的,与您使用强制转换编写的代码不同。不要使用演员!

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

当你像这样投射:

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

它返回一个Comparator<Object>,这意味着你可以在尝试对任何类型的列表进行排序时给出该比较器,例如:一个List<Integer>。它将编译,但在运行时失败。使用强制转换使代码不是类型安全的。正如我所说,不要这样做。


3
投票

而不是使用实现功能Comparator接口的匿名lambda,只需使用一个直接的比较器:

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

3
投票

编辑关于为什么不推断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的左侧分支。

恰好是类型推断算法总是以自上而下的方式在本地工作。一旦它下降到左子树并且无法推断p的类型,它将不会向上走回树并在右子树中搜索其他提示。这实现起来太复杂了,类型检查器越远离有问题的绑定器p,关于失败类型推断的错误消息就越不清楚。类型推断算法不会尝试全局地检查整个程序。


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

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

做你想要的。

© www.soinside.com 2019 - 2024. All rights reserved.