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

eivnm1vs  于 2023-03-06  发布在  Java
关注(0)|答案(8)|浏览(144)
    • 此问题在此处已有答案**:

(17个答案)
八年前就关门了。
在Java中,如何将一个函数作为另一个函数的参数传递?

khbbv19g

khbbv19g1#

Java 8及更高版本

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

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();

使用method reference syntax可使其更加整洁:

new Thread(this::someMethod).start();
  • 如果没有 * lambda表达式,最后两个示例将如下所示:
new Thread(new Runnable() { someMethod(); }).start();

在Java 8之前

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

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

这种模式被称为Command Pattern
请记住,你最好为你的特定用途创建一个接口,如果你选择使用callable,那么你可以用你期望的任何类型的返回值来替换上面的T,比如String。
在回应你的评论下面,你可以说:

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中与函数指针基本概念上的等价物。

nkhmeac6

nkhmeac62#

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

}
vs3odd8k

vs3odd8k3#

Lambda表达式

要添加到jk.'s excellent answer,现在可以使用Lambda Expressions更轻松地传递方法(在Java 8中)。首先,介绍一些背景知识。* functional interface * 是一个具有且只有一个抽象方法的接口,尽管它可以包含任意数量的default methodslambda表达式可以快速实现抽象方法,如果不使用lambda表达式,则不需要所有不必要的语法。
不使用lambda表达式:

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

使用lambda表达式:

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

以下是the Java tutorial on Lambda Expressions的摘录:

Lambda表达式的语法

lambda表达式由以下内容组成:

  • 括在括号中的形参的逗号分隔列表。CheckPerson。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
    }
}

使用:: operator可以进一步缩短上面的示例。

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

ig9co6j14#

多亏了Java 8,你不需要做下面的步骤来传递一个函数给一个方法,这就是lambda的作用,请参阅Oracle's Lambda Expression tutorial.这篇文章的其余部分描述了我们在过去糟糕的日子里为了实现这个功能而必须做的事情。
通常,你声明你的方法是用一个方法接受某个接口,然后你传入一个实现该接口的对象。一个例子是在commons-collections中,你有闭包、Transformer和Predicate的接口,以及你传入这些接口实现的方法。Guava是新改进的commons-collections,你可以在那里找到等价的接口。
例如,commons-collections有org.apache.commons.collections.CollectionUtils,它有很多静态方法来接收传入的对象,随机选择一个,有一个叫exists的,签名是这样的:

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

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

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

并且它根据someCollection是否包含 predicate 为其返回真的对象来返回真或假。
无论如何,这只是一个例子,commons-collections已经过时了,我只是忘记了在Guava中的等价物。

2vuwiymt

2vuwiymt5#

Java对闭包的支持很好,只是不支持 functions,所以你所使用的闭包语法要笨拙得多。你必须用一个方法把所有的东西 Package 在一个类里。2例如,

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

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

woobm2wo

woobm2wo6#

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

public interface Callable<I, O> {

    public O call(I input);   
}
8gsdolmq

8gsdolmq7#

我知道这是一个相当老的职位,但我有另一个稍微简单的解决方案。你可以创建另一个类内,并使其抽象。下一步使抽象方法命名它,无论你喜欢。在原来的类中,使一个方法,把新的类作为一个参数,在这个方法中调用抽象方法。它会看起来像这样。

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)

就像我说的,我知道它很旧,但这种方式我认为更容易一点。希望它有帮助。

vltsax25

vltsax258#

Java目前还不支持闭包,但是有一些其他的语言,比如Scala和Groovy,它们运行在JVM中并且支持闭包。

相关问题