【问题标题】:Is this a limitation of java lambda in design这是 java lambda 在设计中的限制吗
【发布时间】:2018-04-07 07:20:06
【问题描述】:

我想定义一个简单的 lambda,三个整数相加:

(int a, int b,int c)->a+b+c

对于这个简单的案例,我有两个选择:

  1. 定义一个功能接口。单个抽象方法应该是int add(int a, int b,int c),方法名和变量名无所谓。

  2. 使用方法参考。我必须定义一个类/方法或使用具有签名 int add(int a, int b,int c)

  3. 的现有方法

在这两种情况下,对于非常简单的 lambda,我必须回到 OOP 世界(接口、类等)

但是在scala中,在原地定义一个函数是非常简单的: val add= (a:Int,b:Int,c:Int)=>a+b+c

【问题讨论】:

  • 实际上,在 Scala 中也是如此。 lambda 需要有对应的接口。不同之处在于 Scala 标准库已经包含许多这样的接口,即 Function0 ... Function22 特征。 Java 标准库包含许多预制接口,只是数量少得多。 (而且,由于void 和原语,需要更多。例如,Scala 中有 one 接口用于具有 2 个参数的函数,但在 Java 中,您需要 810 个接口来实现相同的东西。)
  • Scala 有两个很棒的东西,它们对最终用户非常友好。 1. 用户可以直接使用 X=>Y 作为函数类型 2. 原始类型 int,..void 都是抽象过来的,所以对最终用户来说没有太多多余的东西。

标签: scala java-8


【解决方案1】:

实际上,在 Scala 中定义函数的工作方式与选项 1 完全相同,除了 1) 函数接口 scala.Function3[A1, A2, A3, B] 包含在标准 Scala 库中; 2)编译器在泛型中伪造支持Int(最终被转换为装箱的java.lang.Integer

对于 2) 在 Java 中你真的无能为力,但 1) 可以通过使用定义 Function3 等的库轻松解决(或编写自己的)。有http://www.functionaljava.org/http://www.vavr.io/https://mvnrepository.com/artifact/uk.org.potes.java8/java8-currying(只有这些接口,仅此而已)。可能还有其他几个。

Scala 在类型推断方面获胜,当然,有两种方式:

  1. FunctionN 是 lambda 表达式的“默认”功能接口,因此无需指定。

  2. 如果指定参数,通常可以推断返回类型。

但在将 lambda 分配给字段或变量时,这些考虑更重要,而在将它们传递给方法时则不太重要,这可能更常见。

【讨论】:

  • 但是Function3,3个输入参数的类型是T1,T2,T3,返回值是R。这个定义有个尴尬的情况,返回值可以不要作废,Function3 print = (Integer a, Integer b, Integer c)->System.out.println("" + a+b+c) 不会编译。对于这种情况,我必须定义 Consumer3
  • 是的,这些也是。
  • 关键是,期望某个函数作为参数的方法必须指定描述预期函数形状的参数类型。这就是功能接口的用途。另一方面,将函数指定为 lambda 表达式的调用者不需要指定类型。
  • 在 Java 中,您可以创建一个函数接口,该接口使用 int 专门避免装箱,而 Function3 需要装箱原语。在 Scala 中是否可以通过使用专门的 SAM 接口而不是常规 Function3 来避免装箱?
  • @puhlen 是的,我相信它的工作方式相同(从 Scala 2.12 开始)。不过,我自己并没有检查生成的字节码。
【解决方案2】:

在 Java 中,您必须将每个 lambda 表达式定位到特定的函数式接口类型。这是设计使然,实际上这意味着函数不是该语言的一等公民。

所以,是的,你必须回到 OOP 世界。

但是,您可以使用 Function 内置类型以 currified 形式定义您的函数:

Function<Integer, Function<Integer, Function<Integer, Integer>>> add =
    a -> b -> c -> a + b + c;

用法:

int result = add.apply(1).apply(2).apply(3); // 6

虽然我怀疑这是否可读...

【讨论】:

  • 谢谢。这是我问题的关键点:函数不是语言的一等公民,
  • 甚至更短,并且没有装箱开销:IntFunction&lt;IntBinaryOperator&gt; add = a -&gt; (b, c) -&gt; a + b + c;int result = add.apply(1).applyAsInt(2, 3);...
【解决方案3】:

您正在寻找以下内容:

int sum = IntStream.of(a, b, c).sum();

【讨论】:

    【解决方案4】:

    在您的特定用例中,操作类型和操作数在时间上都相同(a+b+c => a + (b+c)),您可以使用 BiFunction 定义为:-

    BiFunction<Integer, Integer, Integer> f = (x, y) -> x + y;
    System.out.println(f.apply(a, f.apply(b, c))); // a + b + c
    

    或者@Igor 建议的另一个替代方法是使用sum 而不是IntStream

    【讨论】:

    • @Tom 我猜你在可读性方面的比较是内置方法和自定义方法。在这种情况下(1)在您的问题中,在相同的情况下定义 FI 并接受确定数量的 args 是我希望有一定的灵活性或(2)对于有可变数字的用例args。
    • @Tom 此外,我看待它的方式是定义一个函数。如果一个模块化函数在应用它一次、两次、三次...... N 次时执行一些操作,我可能不会想出它的实现 N 次,除非我发现我的应用程序从中受益。因此,当 Java 可以提出称为 TriFunction、QuadFunction.. 的东西时,问题将是,在复杂性与便利性方面是否需要它。
    • 这篇文章怎么样? stackoverflow.com/questions/30125296/…
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-11-05
    • 2012-09-06
    • 2011-08-01
    • 1970-01-01
    相关资源
    最近更新 更多