将函数作为另一个函数的值传递

Pass function as value for another function

提问人:Voxel 提问时间:8/21/2023 更新时间:8/21/2023 访问量:121

问:

我一直在试图找出如何做到这一点,但似乎找不到一个好的结果。

我正在尝试做的是在另一个函数中传递一个函数,然后获得结果(在实际代码中,它将在一个循环中,因此重点是在更新时获取值,即使该值是私有的)。

所以代码(伪代码)

String myFunction(Function otherFunction) {
    return otherFunction.call();
}

String anotherFunction() {
    return "Hello World!";
}

会让它如此奔跑,会返回“Hello World!myFunction(anotherFunction);

Java 函数 循环

评论

0赞 tobias_k 8/21/2023
(1) 你必须使用 or , (2) 你的函数不是真正的函数,而是一个函数,因为它不带参数。YourClass::anotherFunctionsomeInstance::anotherFunctionSupplier
0赞 Melron 8/21/2023
实际上,您可以将方法作为 Runnable 传递,然后使用 run 方法来执行它。myFunction(() -> anotherFunction())
0赞 Voxel 8/21/2023
@Melron 这适用于调用函数,但是一个空。我特别需要获得输出。
1赞 Kayaman 8/21/2023
用。Callable<String>

答:

0赞 Voxel 8/21/2023 #1

我想出了如何做到这一点(感谢 @Kayaman 和 @Melron)

以下是它的工作原理,具有相同的 2 个功能:

String myFunction(Callable<Object> otherFunction) { // It's Callable<Object> because I want to be able to accept any type as the output - you can also use Callable<String> to accept only functions that output Strings.
    try {
        return otherFunction.call();
    } catch (Exception e) {
        return null;
    }
}

String anotherFunction() {
    return "Hello World!";
}

为了调用它,你会做.myFunction(() -> anotherFunction())

评论

0赞 WJS 8/21/2023
检查我的答案,了解 .无需指定返回类型,因为编译器会计算出它。Callable
1赞 FeXa 8/21/2023 #2

您可以使用如下所示的接口方法解决此问题(这更合理)。

interface MyFunction {
    String call();
}

class AnotherFunction implements MyFunction {
    @Override
    public String call() {
        return "Hello World!";
    }
}

public class Main {
    public static String myFunction(MyFunction otherFunction) {
        return otherFunction.call();
    }

    public static void main(String[] args) {
        MyFunction anotherFunction = new AnotherFunction();
        String result = myFunction(anotherFunction);
        System.out.println(result); // "Hello World!"
    }
}

或者您也可以使用 java.util.function 包,如下所示。

import java.util.function.Supplier;

public class Main {

    public static String myFunction(Supplier<String> otherFunction) {
        return otherFunction.get();
    }

    public static String anotherFunction() {
        return "Hello World!";
 }

    public static void main(String[] args) {
        String result = myFunction(Main::anotherFunction);
        System.out.println(result);
    }
}
0赞 rzwitserloot 8/21/2023 #3

Java 是完全类型的。因此,作为一个论点是不会飞的:这个函数需要多少个参数,它的类型是什么?这个函数是否返回任何内容,如果返回,它有什么类型? 不包括其中任何一个。因此,这是行不通的。FunctionFunction

Java 采取了一种独特的方法来使代码可引用(即“传递函数”)。你首先描述你想要接受的精确内容(不仅仅是根据参数类型和返回类型的#,而且在语义上:你给它一个名字)。您可以在所谓的函数式接口中执行此操作 - 这是一个只有 1 个方法的接口(在剥离任何本身已经定义的内容以及任何带有 impl 的内容之后)。你可以用 ;- 未注释的此类接口(使用 1 个方法)仍然是功能接口;注解只是作为编译器检查的文档(因为如果它不是,将拒绝编译它)。Objectdefault@FunctionalInterfacejavac

所以,让我们做一个。对于这个假设的场景,我们有一个简单的计算器,我们想表示当有人按下功能按钮时要执行的动作。这是一个有点奇怪的计算器;它只处理整数。

这将在您的代码中:

@FunctionalInterface
public interface IntCalculatorOp {
  public int calculate(int a, int b);
}

你可以把它看好并引入一元和二进制运算的概念,但让我们保持简单,因为这只是一个展示这一切如何工作的示例;此计算器仅执行二进制运算。

您现在可以定义计算器本身:

class IntCalculator {
    private final Map<String, IntCalculatorOp> ops = new HashMap<>();
    
    public void registerOp(String button, IntCalculatorOp op) {
        this.ops.put(button, op);
    }
    
    // I wouldn't use Stack in a serious project - just an example!
    private Stack<Integer> stack = new Stack<>();
    
    public void enterNumber(int num) {
        stack.push(num);
    }
    
    public void pressButton(String button) {
        int b = stack.pop();
        int a = stack.pop();
        stack.push(ops.get(button).calculate(a, b));
    }
    
    public int get() {
        return stack.pop();
    }
    public static void main(String[] args) {
        var calc = new IntCalculator();
        calc.registerOp("+", (a, b) -> a + b);
        calc.registerOp("*", (a, b) -> a * b);
        
        calc.enterNumber(5);
        calc.enterNumber(2);
        calc.pressButton("+");
        System.out.println("5 + 2 = " + calc.get());
        calc.enterNumber(4);
        calc.enterNumber(3);
        calc.pressButton("*");
        System.out.println("4 * 3 = " + calc.get());
    }
}

几个关键方面:

  • Lambda 语法(这些东西)要求您将它们放在编译器可以确定它们应该是哪个函数接口的上下文中。该方法的第二个参数是函数式接口,这就是它工作的原因。你不能写,因为这不足以让编译器猜测你想要一个 IntCalculatorOp。(a, b) -> a + bregisterOpIntCalculatorOpObject o = (a, b) -> a + b;

  • 该软件包具有一些通用函数类型。这些只有在你有一个高度通用的概念时才适用,比如“对这个列表中的所有事情做一件事”(例如,.java 自己有接受此类参数的方法)。如果你能更具体地定义一个事物的作用(例如“它代表我的纯整数计算器上的一个按钮”),你应该为它创建一个类型。Java 是名义上的和类型的,使用语言,不要试图与它的内容作斗争。java.util.functionStream

  • Function 可以使用,但需要添加泛型,否则编译器无法知道类型必须是什么。,例如(例如,传递给将该类型作为参数的方法的 lambda 可以采用 或 的形式,或者 - 它是一个接受 1 个 String 类型的参数并返回 Integer 类型的值的函数)。 不过,可能更合适。Function<String, Integer>String::lengthx -> x.length()ToIntFunction<String>

1赞 WJS 8/21/2023 #4

如果您只是返回一个值,那么为什么不使用 .然后,您可以返回相应的类型。Supplier<T>

String result1 = myFunction(this::anotherFunction);
int result2 = myFunction(this::anotherFunction2);
System.out.println(result1);
System.out.println(result2);

指纹

Hello, World
23
public  <T> T myFunction(Supplier<T> otherFunction) {
    return otherFunction.get();
}

public String anotherFunction() {
     return "Hello, World";
}
public Integer anotherFunction2() {
    return 23;
}