【问题标题】:method reference vs lambda expression方法参考与 lambda 表达式
【发布时间】:2016-10-24 09:58:08
【问题描述】:

我想在下面的例子中用方法引用替换 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 表达式转换为方法引用。

【问题讨论】:

  • 如果参数在实例调用中,则不能使用::。您可以将s -&gt; "hi".equals(s) 替换为"hi"::equals,但如果它是s -&gt; s.equals("hi"),则不能
  • 幕后 MethodHandle can shuffle arguments,但这并没有在 java 方法参考语法中公开,我想知道在 inling 之后它是否真的会更快。另一方面,scala 可以通过部分应用函数来做到这一点
  • @the8472: MethodHandle 可以随机播放参数,但结果不再是直接方法句柄,LambdaMetaFactory 仅支持直接方法句柄。另一方面,部分应用的函数会起作用,因为它们不会打乱参数,并且 LMF 支持从左到右的参数绑定。所以对于.endsWith("."),应该绑定正确的参数,没有机会……
  • 方法引用的语法是String::endsWith,加上一些假设的方式来绑定"." 参数。比s -&gt; s.endsWith(".") 有什么优势?

标签: java lambda java-8 method-reference


【解决方案1】:

没有办法“将任何 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 -&gt; s.endsWith(".") 也已经是表达该意图的最小语法。我怀疑您是否能找到一个仍然与 Java 编程语言的其余部分兼容的更小结构。

【讨论】:

  • 为什么当我使用:供应商 x=String::toUpperCase;我得到“无法从字符串类型对非静态方法 toUpperCase() 进行静态引用”但是当我使用 BiPredicate x=String::endsWith;这工作很好
  • toUpperCase 应该如何成为Supplier?它需要 String 才能转换为大写字母,因此您必须使用使用 String 的函数类型,例如Function&lt;String,String&gt;UnaryOperator&lt;String&gt;。或者你绑定了一个像Supplier&lt;String&gt; x="foo"::toUpperCase 这样的实例,但是在始终提供"FOO" 的供应商中并没有多大用处……
【解决方案2】:

您可以使用Eclipse Collections 中的selectWith()selectWith() 采用 Predicate2,它采用 2 个参数而不是 PredicateselectWith() 的第二个参数在每次调用时作为第二个参数传递给 Predicate2,迭代中的每个项目一次。

MutableList<String> words = Lists.mutable.with("toto.", "titi.", "other");
words.selectWith(String::endsWith, ".").each(System.out::println);

默认情况下 Eclipse Collections 是 Eager 的,如果你想延迟迭代,可以使用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 的贡献者。

【讨论】:

    猜你喜欢
    • 2023-04-05
    • 2015-06-23
    • 1970-01-01
    • 2017-10-30
    • 2013-07-16
    • 1970-01-01
    • 2011-06-23
    • 1970-01-01
    • 2016-12-25
    相关资源
    最近更新 更多