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

doinxwow  于 2021-08-20  发布在  Java
关注(0)|答案(8)|浏览(296)

此问题已在此处找到答案

java pass方法作为参数(16个答案)
7年前关闭。
在java中,如何将一个函数作为另一个函数的参数传递?

4zcjmb1e

4zcjmb1e1#

java 8及以上版本

使用java 8+lambda表达式,如果您的类或接口只有一个抽象方法(有时称为sam类型),例如:

public interface MyInterface {
    String doSomething(int param1, String param2);
}

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

class MyClass {
    public MyInterface myInterface = (p1, p2) -> { return p2 + p1; };
}

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

new Thread(() -> someMethod()).start();

并使用方法引用语法使其更清晰:

new Thread(this::someMethod).start();

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

new Thread(new Runnable() { someMethod(); }).start();

java 8之前

一种常见的模式是在接口中“ Package ”它,如 Callable ,例如,然后传入一个可调用的:

public T myMethod(Callable<T> func) {
    return func.call();
}

此模式称为命令模式。
请记住,最好为您的特定用途创建一个接口。如果您选择使用callable,那么您将使用您期望的任何类型的返回值(如string)替换上面的t。
对于您在下面的评论,您可以说:

public int methodToPass() { 
        // do something
}

public void dansMethod(int i, Callable<Integer> myFunc) {
       // do something
}

然后调用它,可能使用匿名内部类:

dansMethod(100, new Callable<Integer>() {
   public Integer call() {
        return methodToPass();
   }
});

请记住,这不是一个“把戏”。它只是java与函数指针的基本概念等价物。

ljsrvy3e

ljsrvy3e2#

您可以使用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);
    }

}
kfgdxczn

kfgdxczn3#

lambda表达式

为了补充jk的优秀答案,现在可以使用lambda表达式(在Java8中)更轻松地传递一个方法。首先是一些背景。函数接口是一个只有一个抽象方法的接口,尽管它可以包含任意数量的默认方法(Java8中新增的)和静态方法。lambda表达式可以快速实现抽象方法,如果不使用lambda表达式,则不需要所有不必要的语法。
没有lambda表达式:

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

使用lambda表达式:

obj.aMethod(i -> i == 982);

以下是关于lambda表达式的java教程的摘录:

lambda表达式的语法

lambda表达式由以下内容组成:
用逗号分隔的形式参数列表,用括号括起来。test方法包含一个参数p,它表示person类的一个示例。
注意:可以省略lambda表达式中参数的数据类型。此外,如果只有一个参数,则可以省略括号。例如,以下lambda表达式也有效:

p -> p.getGender() == Person.Sex.MALE 
    && p.getAge() >= 18
    && p.getAge() <= 25

箭头标记, -> 一种主体,由单个表达式或语句块组成。此示例使用以下表达式:

p.getGender() == Person.Sex.MALE 
    && p.getAge() >= 18
    && p.getAge() <= 25

如果指定一个表达式,java运行时将对该表达式求值,然后返回其值。或者,您可以使用return语句:

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

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

email -> System.out.println(email)

请注意,lambda表达式看起来很像方法声明;可以将lambda表达式视为匿名方法,没有名称。
下面是如何使用lambda表达式“传递方法”:
注:这使用了新的标准功能接口, java.util.function.IntConsumer .

class A {
    public static void methodToPass(int i) { 
        // do stuff
    }
}
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) */
    }
}
class C {
    B b = new B();

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

使用 :: 操作人员

public C() {
    b.dansMethod(100, A::methodToPass);
}
cbeh67ev

cbeh67ev4#

多亏了Java8,您无需执行以下步骤即可将函数传递给方法,这就是lambda的用途,请参阅oracle的lambda表达式教程。这篇文章的其余部分描述了在过去糟糕的日子里,为了实现这个功能,我们必须做些什么。
通常,将方法声明为使用单个方法获取某个接口,然后传入实现该接口的对象。例如,在commons集合中,您有闭包、转换器和 predicate 的接口,以及将这些接口的实现传递到的方法。guava是新改进的commons集合,您可以在那里找到等价的接口。
例如,commons collections有org.apache.commons.collections.collectionutils,它有很多静态方法,这些方法接收传入的对象,随机选取一个,有一个调用exists的方法,具有以下签名:

static boolean exists(java.util.Collection collection, Predicate predicate)

它接受一个实现接口 predicate 的对象,这意味着它必须有一个接受某个对象并返回布尔值的方法。
所以我可以这样称呼它:

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

它返回true或false,这取决于 someCollection 包含 predicate 为其返回true的对象。
无论如何,这只是一个例子,commons集合已经过时了。我只是忘记了Guava的等价物。

dgjrabp2

dgjrabp25#

java很好地支持闭包。它只是不支持函数,所以您用于闭包的语法要复杂得多:您必须用一个方法将所有内容封装在一个类中。例如

public Runnable foo(final int x) {
  return new Runnable() {
    public void run() {
      System.out.println(x);
    }
  };
}

将返回其 run() 方法“关闭”对象 x 传入,就像在任何支持一流函数和闭包的语言中一样。

balp4ylt

balp4ylt6#

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

public interface Callable<I, O> {

    public O call(I input);   
}
uidvcgyl

uidvcgyl7#

我知道这是一个相当老的帖子,但我有另一个稍微简单一点的解决方案。您可以在中创建另一个类并将其抽象。接下来,创建一个抽象方法,可以随意命名。在原始类中创建一个以新类为参数的方法,在此方法中调用抽象方法。它看起来像这样。

public class Demo {

    public Demo(/.../){

    }

    public void view(Action a){
        a.preform();
    }

    /**
     * The Action Class is for making the Demo
     * View Custom Code
     */
    public abstract class Action {

        public Action(/.../){

        }

        abstract void preform();
    }
}

现在您可以这样做,从类中调用方法。

/...
Demo d = new Demo;
Action a = new Action() {

    @Override
    void preform() {
        //Custom Method Code Goes Here
    }
};

/.../
d.view(a)

就像我说的,我知道它很古老,但我觉得这样比较容易。希望能有帮助。

92vpleto

92vpleto8#

java还不支持闭包。但是还有其他一些语言,比如scala和groovy,它们在jvm中运行,并且确实支持闭包。

相关问题