如何传递和使用任意lambda函数作为参数[重复]

Posted

技术标签:

【中文标题】如何传递和使用任意lambda函数作为参数[重复]【英文标题】:How to pass and use an arbitrary lambda functions as parameter [duplicate] 【发布时间】:2016-01-04 08:47:16 【问题描述】:

我对 Lisp 中的 lambda 函数有很好的了解。 Java 似乎没有 Lisp 那样的灵活性。我必须如何考虑 Java 中的 lambda? 鉴于下面的代码,我该怎么做?

public class Draw 
    GraphicsContext gc;

    static void draw(double x, double y, double w, double h, boolean drawRect) 
        if (drawRect) 
            gc.fillRect(x, y, w, h);
         else 
            gc.strokeRect(x, y, w, h);
        
    

    // How do I do that?
    static void drawWithLambda(double x, double y, double w, double h /**, lambda */) 
        lambda(x, y, w, h);
    

    public static void main(String[] args) 
        draw(0, 0, 50, 50, false); // OK
        drawWithLambda(0, 0, 50, 50, GraphicsContext::fillRect); // Ok?
    

【问题讨论】:

请阅读这篇文章:***.com/questions/13604703/… 【参考方案1】:

Java 中的 Lambda 与 functional interface 的概念结合使用。

典型的例子是FunctionFunction 是一个函数式接口,其函数式方法apply 是一个接受单个参数并返回结果的方法。

您可以创建自己的函数式接口,该接口将定义一个带有 4 个参数且没有返回类型的函数式方法,如下所示:

@FunctionalInterface
interface RectangleDrawer 
    void draw(double x, double y, double w, double h);

FunctionalInterface 注释不是绝对必要的,但它给出了明确的意图)。

然后您可以创建一个符合此功能接口约定的 lambda。典型的lambda syntax 是(method arguments) -> (lambda body)。在此示例中,它将是:(x, y, w, h) -> gc.fillRect(x, y, w, h)。这样可以编译,因为 lambda 声明了 4 个参数并且没有返回类型,所以可以表示为之前定义的 RectangleDrawer 的函数式方法。

你的例子会变成:

static GraphicsContext gc;

public static void main(String[] args) 
    draw(0, 0, 50, 50, (x, y, w, h) -> gc.fillRect(x, y, w, h));
    draw(0, 0, 50, 50, (x, y, w, h) -> gc.strokeRect(x, y, w, h));


static void draw(double x, double y, double w, double h, RectangleDrawer drawer) 
    drawer.draw(x, y, w, h);

在这种特殊情况下,可以使用 method reference 创建 lambda,使用 :: 运算符,这样可以编写更简单的代码:

static GraphicsContext gc;

public static void main(String[] args) 
    draw(0, 0, 50, 50, gc::fillRect);
    draw(0, 0, 50, 50, gc::strokeRect);


static void draw(double x, double y, double w, double h, RectangleDrawer drawer) 
    drawer.draw(x, y, w, h);

【讨论】:

非常感谢。我想我明白了。对于我想作为参数传递的每个函数,我必须定义一个接口,其中只包含一个与我想使用的函数具有相同签名的函数定义。 @user2407434 您不必总是定义接口,您可以使用java.util.function 包中预先存在的接口。【参考方案2】:

您必须指定lambda 方法参数实现的功能接口的类型:

drawWithLambda(double x, double y, double w, double h, SomeFuncInterface lambda) 
    lambda.someMethod (x, y, w, h); // someMethod is the single method that
                                    // SomeFuncInterface implements

为了让这条线工作

drawWithLambda(0, 0, 50, 50, GraphicsContext::fillRect);

GraphicsContext的方法fillRect必须与SomeFuncInterface功能接口的方法签名兼容。

顺便说一句,GraphicsContext::fillRect 是方法引用,而不是 lambda 表达式。您可以传递 lambda 表达式、方法引用或功能接口的任何其他实现(常规类实例、匿名类实例等)。

【讨论】:

GraphicsContext::fillRect 无法编译,因为您无法对非静态方法 fillRect 进行静态引用。您需要使用gc::fillRect,其中gcGraphicsContext 的一个实例。 @Tunaki 我的回答很笼统。我对GraphicsContext类不熟悉,所以不知道fillRect是不是静态的。

以上是关于如何传递和使用任意lambda函数作为参数[重复]的主要内容,如果未能解决你的问题,请参考以下文章

将 lambda 作为参数传递 - 通过引用或值?

将Lambda表达式作为参数传递并解析-在构造函数参数列表中使用Lambda表达式

为啥 lambda auto& 参数选择 const 重载?

在 C# 中将 lambda 函数作为命名参数传递

C ++ 11 lambda函数-如何传递参数

将 lambda 作为模板函数参数传递