【问题标题】:How to pass and use an arbitrary lambda functions as parameter [duplicate]如何传递和使用任意lambda函数作为参数[重复]
【发布时间】: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?
    }
}

【问题讨论】:

标签: java lambda java-8


【解决方案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是不是静态的。
猜你喜欢
  • 2020-08-03
  • 2014-10-27
  • 2021-04-14
  • 2014-03-26
  • 1970-01-01
  • 2018-10-26
  • 1970-01-01
  • 2021-12-13
  • 2023-04-03
相关资源
最近更新 更多