如何在Java中将函数作为参数传递?

Jas*_*son 395 java

是否可以将方法作为参数传递给Java方法?如果是这样,有人可以指导我吗?这似乎并不重要

jk.*_*jk. 434

Java 8及以上版本

使用Java 8+ lambda表达式,如果您的类或接口只有一个方法,例如:

public interface MyInterface {
    String doSomething(int param1, String param2);
}
Run Code Online (Sandbox Code Playgroud)

然后在任何使用MyInterface的地方,您可以替换lambda表达式:

class MyClass {
    public MyInterface myInterface = (p1, p2) -> { return p2 + p1; };
}
Run Code Online (Sandbox Code Playgroud)

例如,您可以非常快速地创建新线程:

new Thread(() -> someMethod()).start();
Run Code Online (Sandbox Code Playgroud)

并使用方法参考语法使其更清晰:

new Thread(this::someMethod).start();
Run Code Online (Sandbox Code Playgroud)

如果没有 lambda表达式,最后两个示例将如下所示:

new Thread(new Runnable() { someMethod(); }).start();
Run Code Online (Sandbox Code Playgroud)

在Java 8之前

一个常见的模式是将它"包装"在一个接口中,Callable例如,然后传入一个Callable:

public T myMethod(Callable<T> func) {
    return func.call();
}
Run Code Online (Sandbox Code Playgroud)

此模式称为命令模式.

请记住,最好为特定用途创建界面.如果您选择使用callable,那么您将使用您期望的任何类型的返回值替换上面的T,例如String.

根据您的评论,您可以说:

public int methodToPass() { 
        // do something
}

public void dansMethod(int i, Callable<Integer> myFunc) {
       // do something
}
Run Code Online (Sandbox Code Playgroud)

然后调用它,也许使用匿名内部类:

dansMethod(100, new Callable<Integer>() {
   public Integer call() {
        return methodToPass();
   }
});
Run Code Online (Sandbox Code Playgroud)

请记住,这不是一个"技巧".它只是java的基本概念等价于函数指针.

  • 因此,对于我需要执行此操作的100多种方法,我需要100多个接口.ggreatttttt = / (8认同)
  • @XAleXOwnZX你可以使用一个Callable <>接口,但你需要创建100多个Callable <>类型的匿名内部类.当然,您现在可以使用Java 8 lambda表达式来简化此操作,这在我编写此响应时不可用. (3认同)
  • 我需要一个更简单的解决方案。有人可以帮忙吗? (2认同)
  • @TomeeNS - 这是一个简单的例子,其中 foo() 是作为参数传递的函数,而 demo() 是将函数作为参数的函数。void demo(final Callable&lt;Void&gt; func){ func.call(); } void foo(){ 返回空值;} demo(new Callable&lt;Void&gt;() {public void call() {return foo();}} (2认同)

bdo*_*han 122

您可以使用Java反射来执行此操作.该方法将表示为java.lang.reflect.Method的一个实例.

import java.lang.reflect.Method;

public class Demo {

    public static void main(String[] args) throws Exception{
        Class[] parameterTypes = new Class[1];
        parameterTypes[0] = String.class;
        Method method1 = Demo.class.getMethod("method1", parameterTypes);

        Demo demo = new Demo();
        demo.method2(demo, method1, "Hello World");
    }

    public void method1(String message) {
        System.out.println(message);
    }

    public void method2(Object object, Method method, String message) throws Exception {
        Object[] parameters = new Object[1];
        parameters[0] = message;
        method.invoke(object, parameters);
    }

}
Run Code Online (Sandbox Code Playgroud)

  • @Tim Bender:或者在"镜子"里面? (10认同)

The*_*Hat 95

Lambda表达式

要添加jk.的优秀答案,您现在可以使用Lambda Expressions(在Java 8中)更轻松地传递方法.首先,一些背景.甲功能接口是具有一个且只有一个抽象方法的接口,尽管它可以包含任意数量的默认方法和静态方法(在Java中8个新).如果不使用lambda表达式,lambda表达式可以快速实现抽象方法,而不需要所有不必要的语法.

没有lambda表达式:

obj.aMethod(new AFunctionalInterface() {
    @Override
    public boolean anotherMethod(int i)
    {
        return i == 982
    }
});
Run Code Online (Sandbox Code Playgroud)

使用lambda表达式:

obj.aMethod(i -> i == 982);
Run Code Online (Sandbox Code Playgroud)

以下是Lambda表达式Java教程的摘录:

Lambda表达式的语法

lambda表达式包含以下内容:

  • 括号中用逗号分隔的形式参数列表.CheckPerson.test方法包含一个参数p,它表示Person类的实例.

    注意:您可以省略lambda表达式中参数的数据类型.此外,如果只有一个参数,则可以省略括号.例如,以下lambda表达式也是有效的:

    p -> p.getGender() == Person.Sex.MALE 
        && p.getAge() >= 18
        && p.getAge() <= 25
    
    Run Code Online (Sandbox Code Playgroud)
  • 箭头标记, ->

  • 一个主体,由单个表达式或语句块组成.此示例使用以下表达式:

    p.getGender() == Person.Sex.MALE 
        && p.getAge() >= 18
        && p.getAge() <= 25
    
    Run Code Online (Sandbox Code Playgroud)

    如果指定单个表达式,则Java运行时将计算表达式,然后返回其值.或者,您可以使用return语句:

    p -> {
        return p.getGender() == Person.Sex.MALE
            && p.getAge() >= 18
            && p.getAge() <= 25;
    }
    
    Run Code Online (Sandbox Code Playgroud)

    return语句不是表达式; 在lambda表达式中,必须将语句括在大括号({})中.但是,您不必在大括号中包含void方法调用.例如,以下是有效的lambda表达式:

    email -> System.out.println(email)
    
    Run Code Online (Sandbox Code Playgroud)

请注意,lambda表达式看起来很像方法声明; 您可以将lambda表达式视为匿名方法 - 没有名称的方法.


以下是使用lambda表达式"传递方法"的方法:

注意:这使用了新的标准功能接口java.util.function.IntConsumer.

class A {
    public static void methodToPass(int i) { 
        // do stuff
    }
}
Run Code Online (Sandbox Code Playgroud)
import java.util.function.IntConsumer;

class B {
    public void dansMethod(int i, IntConsumer aMethod) {
        /* you can now call the passed method by saying aMethod.accept(i), and it
        will be the equivalent of saying A.methodToPass(i) */
    }
}
Run Code Online (Sandbox Code Playgroud)
class C {
    B b = new B();

    public C() {
        b.dansMethod(100, j -> A.methodToPass(j));   //Lambda Expression here
    }
}
Run Code Online (Sandbox Code Playgroud)

使用::操作员可以进一步缩短上述示例.

public C() {
    b.dansMethod(100, A::methodToPass);
}
Run Code Online (Sandbox Code Playgroud)


Nat*_*hes 14

感谢Java 8,您不需要执行以下步骤将函数传递给方法,这就是lambdas的用途,请参阅Oracle的Lambda Expression教程.本文的其余部分描述了我们过去在过去的糟糕时期必须要做的事情,以实现此功能.

通常,您将方法声明为使用单个方法接受某个接口,然后传入实现该接口的对象.一个例子是在commons-collections中,你有Closure,Transformer和Predicate的接口,以及你将它们的实现传递到的方法.Guava是新改进的commons-collections,你可以在那里找到相应的接口.

因此,例如,commons-collections有org.apache.commons.collections.CollectionUtils,它有许多传入对象的静态方法,随机选择一个,有一个带有此签名的exists存在:

static boolean exists(java.util.Collection collection, Predicate predicate) 
Run Code Online (Sandbox Code Playgroud)

它需要一个实现接口Predicate的对象,这意味着它必须有一个方法,它接受一些Object并返回一个布尔值.

所以我可以这样称呼它:

CollectionUtils.exists(someCollection, new Predicate() {
    public boolean evaluate(Object object) { 
        return ("a".equals(object.toString());
    }
});
Run Code Online (Sandbox Code Playgroud)

并且它返回true或false,具体取决于是否someCollection包含谓词返回true的对象.

无论如何,这只是一个例子,公共收藏已经过时了.我忘记了番石榴中的等价物.


ama*_*loy 6

Java支持闭包就好了.它只是不支持函数,所以你习惯用于闭包的语法更加笨拙和笨重:你必须用一个方法将所有东西都包装在一个类中.例如,

public Runnable foo(final int x) {
  return new Runnable() {
    public void run() {
      System.out.println(x);
    }
  };
}
Run Code Online (Sandbox Code Playgroud)

将返回一个Runnable对象,其run()方法"关闭" x传入的内容,就像支持第一类函数和闭包的任何语言一样.


Ara*_*yan 5

我使用了@jk的命令模式.提到,添加一个返回类型:

public interface Callable<I, O> {

    public O call(I input);   
}
Run Code Online (Sandbox Code Playgroud)