方法引用和Lambda表达式的区别

4

我希望在下面的例子中用方法引用替换Lambda表达式:

 public class Example {

        public static void main(String[] args) {
            List<String> words = Arrays.asList("toto.", "titi.", "other");
         //lambda expression in the filter (predicate)
            words.stream().filter(s -> s.endsWith(".")).forEach(System.out::println);
        }
   }

我想写一个类似这样的东西:
words.stream().filter(s::endsWith(".")).forEach(System.out::println);

是否可能将任何Lambda表达式转换为方法引用?


可能是Method references for non-empty arguments?的重复问题。 - fafl
5
在调用实例时,无法使用 :: 来引用方法参数。可以用 "hi"::equals 替换 s -> "hi".equals(s),但是不能用它来替换 s -> s.equals("hi") - Peter Lawrey
2
在底层,MethodHandle 可以重排参数,但这在Java方法引用语法中没有暴露出来,我想知道在内联后它是否会更快。另一方面,Scala可以使用部分应用的函数来实现这一点。 - the8472
2
@the8472: MethodHandle可以重新排列参数,但结果不再是直接的方法句柄,而LambdaMetaFactory仅支持直接方法句柄。另一方面,部分应用的函数将起作用,因为它们不会重新排列参数,并且LMF支持从左到右的参数绑定。因此对于.endsWith("."),其中右参数应该被绑定,机会渺茫... - Holger
1
方法引用的语法将是String::endsWith,再加上一种假设的方法来绑定参数"."。相比于s -> s.endsWith("."),这有什么优势呢? - Holger
显示剩余2条评论
2个回答

4

无法将任何lambda表达式转换为方法引用,但是如果需要反复使用,您可以为特定目标类型实现工厂:

public static <A,B> Predicate<A> bind2nd(BiPredicate<A,B> p, B b) {
    return a -> p.test(a, b);
}

通过这个,你可以编写代码

words.stream().filter(bind2nd(String::endsWith, ".")).forEach(System.out::println);

但实际上,并没有什么优势。从技术上讲,Lambda表达式正好做到了你想要的,最小化必要参数转换代码,表达为Lambda表达式的主体,编译成合成方法和对该合成代码的方法引用。语法
s -> s.endsWith(".")已经是表达这种意图所需的最小语法。我怀疑你能找到一个更小的构造,它仍然与Java编程语言的其余部分兼容。


为什么当我使用:Supplier<String> x=String::toUpperCase时,会得到"Cannot make a static reference to the non-static method toUpperCase() from the type String"的错误,但是当我使用BiPredicate<String, String> x=String::endsWith时,一切都能正常工作。 - midy62
2
toUpperCase 应该如何成为 Supplier?它需要一个要转换为大写的 String,因此您必须使用消耗 String 的函数类型,例如 Function<String,String>UnaryOperator<String>。或者您可以绑定一个实例,例如 Supplier<String> x="foo"::toUpperCase,但是总是提供 "FOO" 的供应商没有太多用处... - Holger

3
你可以使用Eclipse Collections中的selectWith()selectWith()接受一个Predicate2,该谓词接受两个参数,而不是一个PredicateselectWith()的第二个参数会被传递给Predicate2的第二个参数,每次在可迭代项中调用一次。
MutableList<String> words = Lists.mutable.with("toto.", "titi.", "other");
words.selectWith(String::endsWith, ".").each(System.out::println);

默认情况下,Eclipse Collections是急切的,如果你想进行惰性迭代,则可以使用asLazy()

words.asLazy().selectWith(String::endsWith, ".").each(System.out::println);

如果你不能从 List 进行更改:
List<String> words = Arrays.asList("toto.", "titi.", "other");
ListAdapter.adapt(words).selectWith(String::endsWith, ".").each(System.out::println);

Eclipse Collections的RichIterable有几个其他*With方法,这些方法与方法引用很好地配合使用,包括rejectWith()partitionWith()detechWith()anySatisfyWith()allSatisfyWith()noneSatisfyWith()collectWith()
注意:我是Eclipse Collections的贡献者。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接