如何定义Java 8中将lambda作为参数的方法?


363

在Java 8中,可以将方法创建为Lambda表达式,并可以通过引用将其传递(在后台进行一些工作)。在线上有很多示例,其中创建了lambda并将其与方法一起使用,但没有如何使用lambda作为参数的方法的示例。它的语法是什么?

MyClass.method((a, b) -> a+b);


class MyClass{
  //How do I define this method?
  static int method(Lambda l){
    return l(5, 10);
  }
}

29
好问题。您说得对:这些教程都没有包含这一部分。
马丁

Answers:


247

Lambda纯粹是一个调用站点构造:lambda的接收者不需要知道Lambda涉及到,而是接受具有适当方法的Interface。

换句话说,您定义或使用一个功能接口(即具有单个方法的接口)来接受并准确返回您想要的内容。

为此,Java 8附带了一组常用的接口类型java.util.function(感谢Maurice Naftalin提供有关JavaDoc的提示)。

对于这种特定的用例,java.util.function.IntBinaryOperator只有一个int applyAsInt(int left, int right)方法,因此您可以这样编写method

static int method(IntBinaryOperator op){
    return op.applyAsInt(5, 10);
}

但是您也可以定义自己的接口并像这样使用它:

public interface TwoArgIntOperator {
    public int op(int a, int b);
}

//elsewhere:
static int method(TwoArgIntOperator operator) {
    return operator.op(5, 10);
}

使用您自己的界面的优点是您可以使用更清楚地表明意图的名称。


5
是否会使用内置接口,还是必须为我要使用的每个lambda创建一个接口?
Marius 2012年

可重用性与描述性名称难题的一个很好的折衷方案是扩展内置接口,而不覆盖其指定的方法。这样,只需一行额外的代码即可为您提供描述性名称。
Will Byrne 2015年

我不明白 他可以通过lambda做任何事情,这行得通吗?如果他通过会发生什么(int a, int b, int c)TwoArgIntOperator。如果TwoArgIntOperator具有相同签名的两个方法会发生什么。这个答案令人困惑。
托马什Zato -恢复莫妮卡

7
@TomášZato:如果您使用带有不匹配参数的lambda,则编译器会抱怨。具有两种(非默认)方法的接口将不能用作lambda,因为只能使用功能接口
Joachim Sauer

一个基本问题:我认为我仍然不了解将方法作为参数传递而缺少该方法的参数的方面。如果他要传递TwoArgIntOperator作为参数,并且需要分别传递该方法的参数,这看起来并不难看吗?有没有办法将完整的执行主体与参数一起传递?像您的示例一样,一种避免对“ 5”和“ 10”进行硬编码的方法。
instanceOfObject

63

要使用Lambda表达式,您需要创建自己的功能接口或将Java功能接口用于需要两个整数并作为值返回的操作。IntBinaryOperator

使用用户定义的功能界面

interface TwoArgInterface {

    public int operation(int a, int b);
}

public class MyClass {

    public static void main(String javalatte[]) {
        // this is lambda expression
        TwoArgInterface plusOperation = (a, b) -> a + b;
        System.out.println("Sum of 10,34 : " + plusOperation.operation(10, 34));

    }
}

使用Java功能界面

import java.util.function.IntBinaryOperator;

public class MyClass1 {

    static void main(String javalatte[]) {
        // this is lambda expression
        IntBinaryOperator plusOperation = (a, b) -> a + b;
        System.out.println("Sum of 10,34 : " + plusOperation.applyAsInt(10, 34));

    }
}

我创建的其他示例在这里


IntBinaryOperator文档的链接已失效。
Hendrikto

1
这是到目前为止我发现的lambda最好的例子,并且它是唯一真正让我最终“了解”它的例子。
JimmySmithJR

1
Sooooooo ...基本上是代表,但我们不应该这样称呼它吗?
Ryan Lundy '18

37

对于不超过2个参数的函数,可以在不定义自己的接口的情况下传递它们。例如,

class Klass {
  static List<String> foo(Integer a, String b) { ... }
}

class MyClass{

  static List<String> method(BiFunction<Integer, String, List<String>> fn){
    return fn.apply(5, "FooBar");
  }
}

List<String> lStr = MyClass.method((a, b) -> Klass.foo((Integer) a, (String) b));

在中BiFunction<Integer, String, List<String>>IntegerString是其参数,并且List<String>是其返回类型。

对于仅具有一个参数的函数,可以使用Function<T, R>,其中T参数的类型为,R返回值的类型为。有关Java已经提供的所有接口,请参考此页面


15

http://lambdafaq.org/lambda-resources链接到一个支持Lambda的Java 8 JavaDocs的公共Web访问版本。(显然,这应该是对Joachim Sauer的回答的评论,但是我无法进入我的SO帐户并需要添加评论。) -lambda问题。

注意:此答案是在Java 8 GA文档公开可用之前编写的。不过,我仍然留在原地,因为Lambda FAQ对于学习Java 8中引入的功能的人们可能仍然有用。


2
感谢您提供的链接以及您维护该站点的事实!我自由地将指向您的公共JavaDoc的链接添加到我的答案中。
约阿希姆·绍尔

1
附带说明:看来您正在为Lambdas构建Angelika Langer 为Generics构建的东西。谢谢您,Java需要这样的资源!
约阿希姆·绍尔

1
虽然此链接可以回答问题,但最好在此处包括答案的基本部分,并提供链接以供参考。如果链接的页面发生更改,仅链接的答案可能会失效。- 评论
-ClearLogic

@ClearLogic是的,同意。AFAIR,我不想在现有答案中添加任何内容,而只是指出我在哪里发布了API文档的副本,否则该API文档当时很难访问。
莫里斯·纳夫塔林

7

对我来说,最有意义的解决方案是定义一个Callback接口:

interface Callback {
    void call();
}

然后将其用作您要调用的函数中的参数:

void somewhereInYourCode() {
    method(() -> {
        // You've passed a lambda!
        // method() is done, do whatever you want here.
    });
}

void method(Callback callback) {
    // Do what you have to do
    // ...

    // Don't forget to notify the caller once you're done
    callback.call();
}

虽然只是一个精度

Lambda不是特殊的接口,类或您可以自己声明的其他任何东西。Lambda只是() -> {}特殊语法的名称,当将单方法接口作为参数传递时,可以提高可读性。它旨在替代此:

method(new Callback() {
    @Override
    public void call() {
        // Classic interface implementation, lot of useless boilerplate code.
        // method() is done, do whatever you want here.
    }
});

所以在上面的例子中,Callback不是一个lambda,这只是一个普通的接口; lambda是可用于实现它的快捷方式语法的名称。


6

对于使用谷歌搜索的任何人,最好使用java.util.function.BiConsumer。例如:

Import java.util.function.Consumer
public Class Main {
    public static void runLambda(BiConsumer<Integer, Integer> lambda) {
        lambda.accept(102, 54)
    }

    public static void main(String[] args) {
        runLambda((int1, int2) -> System.out.println(int1 + " + " + int2 + " = " + (int1 + int2)));
    }

输出是:166


1
代替Consumer<Pair<A,B>>BiConsumer<A,B>用于这种情况。(docs
nobar

不知道它的存在,下一次我应该浏览一下功能包。
Big_Bad_E

5

可以将Lambda表达式作为参数传递。要将Lambda表达式作为参数传递,参数的类型(接收lambda表达式作为参数)必须具有功能接口类型。

如果有功能界面-

interface IMyFunc {
   boolean test(int num);
}

并且有一个过滤器方法仅在列表中大于5时才将int添加到列表中。请注意,此处的过滤器方法将函数接口IMyFunc作为参数之一。在这种情况下,可以将lambda表达式作为方法参数的参数传递。

public class LambdaDemo {
    public static List<Integer> filter(IMyFunc testNum, List<Integer> listItems) {
        List<Integer> result = new ArrayList<Integer>();
        for(Integer item: listItems) {
            if(testNum.test(item)) {
                result.add(item);
            }
        }
        return result;
    }
    public static void main(String[] args) {
        List<Integer> myList = new ArrayList<Integer>();
        myList.add(1);
        myList.add(4);
        myList.add(6);
        myList.add(7);
        // calling filter method with a lambda expression
        // as one of the param
        Collection<Integer> values = filter(n -> n > 5, myList);

        System.out.println("Filtered values " + values);
    }
}

2

您可以使用上面提到的功能接口。以下是一些示例

Function<Integer, Integer> f1 = num->(num*2+1);
System.out.println(f1.apply(10));

Predicate<Integer> f2= num->(num > 10);
System.out.println(f2.test(10));
System.out.println(f2.test(11));

Supplier<Integer> f3= ()-> 100;
System.out.println(f3.get());

希望能帮助到你


1

好吧,那很容易。lambda表达式的目的是实现功能接口。它是只有一种方法的接口。这是有关预定义和旧功能接口的出色文章。

无论如何,如果您想实现自己的功能接口,请创建它。仅举一个简单的例子:

public interface MyFunctionalInterface {
    String makeIt(String s);
}

因此,让我们创建一个类,在其中创建一个方法,该方法接受MyFunctionalInterface的类型:

public class Main {

    static void printIt(String s, MyFunctionalInterface f) {
        System.out.println(f.makeIt(s));
    }

    public static void main(String[] args) {

    }
}

您应该做的最后一件事是将MyFunctionalInterface的实现传递给我们定义的方法:

public class Main {

    static void printIt(String s, MyFunctionalInterface f) {
        System.out.println(f.makeIt(s));
    }

    public static void main(String[] args) {
        printIt("Java", s -> s + " is Awesome");
    }
}

而已!


1

Lambda不是对象,而是功能接口。使用@FuntionalInterface作为注释,可以定义尽可能多的功能接口。

@FuntionalInterface
public interface SumLambdaExpression {
     public int do(int a, int b);
}

public class MyClass {
     public static void main(String [] args) {
          SumLambdaExpression s = (a,b)->a+b;
          lambdaArgFunction(s);
     }

     public static void lambdaArgFunction(SumLambdaExpression s) {
          System.out.println("Output : "+s.do(2,5));
     }
}

输出如下

Output : 7

Lambda表达式的基本概念是定义您自己的逻辑,但已经定义了参数。因此,在上面的代码中,您可以将do函数的定义从添加定义更改为任何其他定义,但是您的参数限制为2。


1

基本上要将lamda表达式作为参数传递,我们需要一个可以容纳它的类型。就像整数值一样,我们保存在原始int或Integer类中。Java没有用于lamda表达式的单独类型,而是使用接口作为类型来保存参数。但是该接口应该是功能接口


0

请执行下列操作 ..

您已经声明了method(lambda l) 所有要做的事情,就是使用名称创建一个Interface lambda并声明一个抽象方法

public int add(int a,int b);  

方法名称在这里无关紧要。

因此,当您调用MyClass.method( (a,b)->a+b) 此实现时, (a,b)->a+b它将注入到您的接口add方法中。因此,无论何时调用l.add它,都将采用此实现并执行aand breturn l.add(2,3)返回5。-基本上这就是lambda所做的。


-1

C#处理此问题的大致方式(但表示为Java代码)。这样的事情几乎可以满足您的所有需求:

import static org.util.function.Functions.*;

public class Test {

    public static void main(String[] args)
    {
        Test.invoke((a, b) -> a + b);       
    }

    public static void invoke(Func2<Integer, Integer, Integer> func)
    {
        System.out.println(func.apply(5, 6));
    }
}

package org.util.function;

public interface Functions {

    //Actions:
    public interface Action {
        public void apply();
    }

    public interface Action1<T1> {
        public void apply(T1 arg1);
    }

    public interface Action2<T1, T2> {
        public void apply(T1 arg1, T2 arg2);
    }

    public interface Action3<T1, T2, T3> {
        public void apply(T1 arg1, T2 arg2, T3 arg3);
    }

    public interface Action4<T1, T2, T3, T4> {
        public void apply(T1 arg1, T2 arg2, T3 arg3, T4 arg4);
    }

    public interface Action5<T1, T2, T3, T4, T5> {
        public void apply(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5);
    }

    public interface Action6<T1, T2, T3, T4, T5, T6> {
        public void apply(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6);
    }

    public interface Action7<T1, T2, T3, T4, T5, T6, T7> {
        public void apply(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7);
    }

    public interface Action8<T1, T2, T3, T4, T5, T6, T7, T8> {
        public void apply(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8);
    }

    //Functions:
    public interface Func<TResult> {
        public TResult apply();
    }

    public interface Func1<T1, TResult> {
        public TResult apply(T1 arg1);
    }

    public interface Func2<T1, T2, TResult> {
        public TResult apply(T1 arg1, T2 arg2);
    }

    public interface Func3<T1, T2, T3, TResult> {
        public TResult apply(T1 arg1, T2 arg2, T3 arg3);
    }

    public interface Func4<T1, T2, T3, T4, TResult> {
        public TResult apply(T1 arg1, T2 arg2, T3 arg3, T4 arg4);
    }

    public interface Func5<T1, T2, T3, T4, T5, TResult> {
        public TResult apply(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5);
    }

    public interface Func6<T1, T2, T3, T4, T5, T6, TResult> {
        public TResult apply(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6);
    }

    public interface Func7<T1, T2, T3, T4, T5, T6, T7, TResult> {
        public TResult apply(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7);
    }

    public interface Func8<T1, T2, T3, T4, T5, T6, T7, T8, TResult> {
        public TResult apply(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8);
    }
}

-2

使用lambda作为参数具有灵活性。它启用Java中的函数式编程。基本语法是

参数-> method_body

以下是一种方法,您可以定义一个以功能接口(使用lambda)作为参数的方法。一个。如果您想定义在函数接口内部声明的方法,例如,函数接口作为从以下位置调用的方法的参数/参数给出main()

@FunctionalInterface
interface FInterface{
    int callMeLambda(String temp);
}


class ConcreteClass{

    void funcUsesAnonymousOrLambda(FInterface fi){
        System.out.println("===Executing method arg instantiated with Lambda==="));
    }

    public static void main(){
        // calls a method having FInterface as an argument.
        funcUsesAnonymousOrLambda(new FInterface() {

            int callMeLambda(String temp){ //define callMeLambda(){} here..
                return 0;
            }
        }
    }

/***********Can be replaced by Lambda below*********/
        funcUsesAnonymousOrLambda( (x) -> {
            return 0; //(1)
        }

    }

FInterface fi =(x)-> {return 0; };

funcUsesAnonymousOrLambda(fi);

可以在上面看到如何用接口替换lambda表达式。

上面解释了lambda表达式的一种特殊用法,还有更多。ref lambda中的Java 8 lambda无法修改外部lambda中的变量

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.