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。正如我所说,不要那样做。
我有这段代码,我尝试将方法引用 ("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。正如我所说,不要那样做。