在Java 8中,带有2个箭头的lambda是什么意思?


118

我之前阅读过一些Java 8教程。

现在我遇到以下主题: Java是否支持Currying?

在这里,我看到以下代码:

IntFunction<IntUnaryOperator> curriedAdd = a -> b -> a + b;
System.out.println(curriedAdd.apply(1).applyAsInt(12));

我了解此示例包含2个元素,但无法理解其构造:

a -> b -> a + b;

根据表达式的左侧,该行应实现以下功能:

R apply(int value); 

在此之前,我只遇到一个箭头就遇到了lambda。


6
你明白什么叫骗人吗?这是这个问题不可或缺的部分。
BlueRaja-Danny Pflughoeft

12
只是一个lambda返回lambda。
Ilmari Karonen

Answers:


118

如果将其表示为非速记lambda语法或pre-lambda Java匿名类语法,则更清楚发生了什么...

原来的问题。为什么有两个箭头?很简单,定义了两个函数...第一个函数是函数定义函数,第二个是该函数的结果,该函数也恰好是函数。每个都需要->操作员来定义它。

非速记

IntFunction<IntUnaryOperator> curriedAdd = (a) -> {
    return (b) -> {
        return a + b;
    };
};

Java 8之前的Pre-Lambda

IntFunction<IntUnaryOperator> curriedAdd = new IntFunction<IntUnaryOperator>() {
    @Override
    public IntUnaryOperator apply(final int value) {
        IntUnaryOperator op = new IntUnaryOperator() {
            @Override
            public int applyAsInt(int operand) {
                return operand + value;
            }
        };
        return op;
    }
};

1
预lambda要求final int value
njzk2

8
是的,您是对的,但是我写的仍然是使用Java 8编译器,该编译器允许您使用“有效的最终定义”
Adam

1
@gstackoverflow是,但是Java 8不是pre-lambda
njzk2 2015年

1
您也可以在Java 8中使用pre-lambda样式。我写的是JFY
gstackoverflow

3
难道不是为了让人们都能得分而做过lambda吗?:-)
Stephane

48

An IntFunction<R>是一个函数int -> R。An IntUnaryOperator是一个函数int -> int

因此,IntFunction<IntUnaryOperator>a是将intas作为参数并返回一个将intas作为参数并返回an 的函数int

a -> b -> a + b;
^    |         |
|     ---------
|         ^
|         |
|         The IntUnaryOperator (that takes an int, b) and return an int (the sum of a and b)
|
The parameter you give to the IntFunction

如果您使用匿名类“分解” lambda,也许会更清楚:

IntFunction<IntUnaryOperator> add = new IntFunction<IntUnaryOperator>() {
    @Override
    public IntUnaryOperator apply(int a) {
        return new IntUnaryOperator() {
            @Override
            public int applyAsInt(int b) {
                return a + b;
            }
        };
    }
};

29

添加括号可以使这一点更加清楚:

IntFunction<IntUnaryOperator> curriedAdd = a -> (b -> (a + b));

也许中间变量可能会有所帮助:

IntFunction<IntUnaryOperator> curriedAdd = a -> {
    IntUnaryOperator op = b -> a + b;
    return op;
};

24

让我们用括号重写该lambda表达式以使其更清楚:

IntFunction<IntUnaryOperator> curriedAdd = a -> (b -> (a + b));

因此,我们声明了一个带有的函数,该函数int返回Function。更具体地说,返回的函数采用an int并返回int(两个元素之和):这可以表示为IntUnaryOperator

因此,curriedAdd是一个带an int并返回an 的函数IntUnaryOperator,因此可以表示为IntFunction<IntUnaryOperator>


9

这是两个lambda表达式。

IntFunction<IntUnaryOperator> curriedAdd = 
  a -> { //this is for the fixed value
    return b -> { //this is for the add operation
      return a + b;
    };
  }

IntUnaryOperator addTwo = curriedAdd.apply(2);
System.out.println(addTwo.applyAsInt(12)); //prints 14

8

如果您看一下,IntFunction它可能会变得更加清晰:IntFunction<R>是一个FunctionalInterface。它表示一个函数,该函数采用an int并返回type的值R

在这种情况下,返回类型R也是a FunctionalInterface,即IntUnaryOperator。因此,第一个(外部)函数本身返回一个函数。

在这种情况下:当应用于时intcurriedAdd应该返回一个再次使用的函数int(并再次返回int,因为那样IntUnaryOperator做)。

在函数式编程中,通常将函数的类型写为as,param -> return_value并且您在此处可以看到确切的内容。因此,类型curriedAddint -> int -> int(或int -> (int -> int)您是否更喜欢)。

Java 8的lambda语法与此同时。要定义这样的功能,您可以编写

a -> b -> a + b

这与实际的lambda演算非常相似:

λa λb a + b

λb a + b是一个具有单个参数b并返回值(总和)的函数。λa λb a + b是一个接受单个参数a并返回另一个参数的函数。λa λb a + b返回λb a + b,并将其a设置为参数值。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.