是否可以将方法作为参数传递给Java方法?如果是这样,有人可以指导我吗?这看起来并不简单
答案 0 :(得分:410)
使用Java 8+ lambda表达式,如果您的类或接口只有一个方法,例如:
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();
一个常见的模式是将它“包裹”在一个界面中,例如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的基本概念等价于函数指针。
答案 1 :(得分:123)
您可以使用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);
}
}
答案 2 :(得分:90)
要添加到jk.'s excellent answer,您现在可以使用Lambda Expressions(在Java 8中)更轻松地传递方法。首先,一些背景。 功能接口是一个只有一个抽象方法的接口,尽管它可以包含任意数量的default methods(Java 8中的新增功能)和静态方法。 lambda表达式可以快速实现抽象方法,如果不使用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);
}
答案 3 :(得分:14)
感谢Java 8,您不需要执行以下步骤将函数传递给方法,这就是lambdas的用途,请参阅Oracle's Lambda Expression tutorial。本文的其余部分描述了我们过去在过去的糟糕时期必须做的事情,以实现此功能。
通常,您将方法声明为使用单个方法接受某个接口,然后传入实现该接口的对象。一个例子是在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)
它需要一个实现接口Predicate的对象,这意味着它必须有一个方法,它接受一些Object并返回一个布尔值。
所以我可以这样称呼它:
CollectionUtils.exists(someCollection, new Predicate() {
public boolean evaluate(Object object) {
return ("a".equals(object.toString());
}
});
并返回true或false,具体取决于someCollection
是否包含谓词返回true的对象。
无论如何,这只是一个例子,公共收藏已经过时了。我只是忘记了番石榴中的等效物。
答案 4 :(得分:5)
Java支持闭包很好。它只是不支持 functions ,所以你习惯用于闭包的语法更加笨拙和笨重:你必须用一个方法将所有东西都包装在一个类中。例如,
public Runnable foo(final int x) {
return new Runnable() {
public void run() {
System.out.println(x);
}
};
}
将返回一个Runnable对象,其run()
方法“关闭”传入的x
,就像支持第一类函数和闭包的任何语言一样。
答案 5 :(得分:5)
我使用了@jk的命令模式。提到,添加一个返回类型:
public interface Callable<I, O> {
public O call(I input);
}
答案 6 :(得分:4)
我知道这是一个相当古老的帖子,但我有另一个稍微简单的解决方案。 您可以在其中创建另一个类并使其成为抽象类。接下来,使用任何您喜欢的方法将Abstract方法命名为。在原始类中创建一个以新类作为参数的方法,在此方法中调用抽象方法。它看起来像这样。
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)
就像我说的那样,我知道它的陈旧但这种方式我觉得有点容易。希望它有所帮助。
答案 7 :(得分:-10)
Java(尚未)支持闭包。但是还有其他语言,如Scala和Groovy,它们在JVM中运行并支持闭包。