Java Pass方法作为参数

我正在寻找一种通过引用来传递方法的方法。 我明白,Java不会传递方法作为参数,但是,我想获得一个替代scheme。

我已经被告知接口是传递方法作为参数的替代方法,但我不明白接口如何作为引用的方法。 如果我理解正确,一个接口就是一个抽象的未定义的方法集合。 我不想发送一个需要每次定义的接口,因为几个不同的方法可能调用相同的参数相同的方法。

我想要做的是类似这样的事情:

public void setAllComponents(Component[] myComponentArray, Method myMethod) { for (Component leaf : myComponentArray) { if (leaf instanceof Container) { //recursive call if Container Container node = (Container) leaf; setAllComponents(node.getComponents(), myMethod); } //end if node myMethod(leaf); } //end looping through components } 

引用如:

 setAllComponents(this.getComponents(), changeColor()); setAllComponents(this.getComponents(), changeSize()); 

编辑 :从Java 8开始, lambdaexpression式是其他 答案指出的一个很好的解决scheme。 下面的答案是为Java 7和更早的版本编写的。


看看命令模式 。

 // NOTE: code not tested, but I believe this is valid java... public class CommandExample { public interface Command { public void execute(Object data); } public class PrintCommand implements Command { public void execute(Object data) { System.out.println(data.toString()); } } public static void callCommand(Command command, Object data) { command.execute(data); } public static void main(String... args) { callCommand(new PrintCommand(), "hello world"); } } 

编辑:正如Pete Kirkham指出的那样 ,还有另外一种使用访问者的方式 。 访问者方法有一点涉及 – 你的节点都需要用acceptVisitor()方法来访问感知 – 但是如果你需要遍历一个更复杂的对象图,那么这是值得研究的。

在Java 8中,现在可以使用Lambdaexpression式更轻松地传递一个方法。 首先是一些背景。 function接口是一个只有一个抽象方法的接口,尽pipe它可以包含任意数量的默认方法 (Java 8中的新增function)和静态方法。 如果不使用lambdaexpression式,lambdaexpression式可以快速实现抽象方法,而不需要所有不必要的语法。

没有lambdaexpression式:

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

用lambdaexpression式:

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

下面是关于Lambdaexpression式的Java教程的摘录:

Lambdaexpression式的语法

一个lambdaexpression式包含以下内容:

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

    注意 :您可以省略lambdaexpression式中参数的数据types。 另外,如果只有一个参数,则可以省略括号。 例如,下面的lambdaexpression式也是有效的:

     p -> p.getGender() == Person.Sex.MALE && p.getAge() >= 18 && p.getAge() <= 25 
  • 箭头标记->

  • 一个由单个expression式或语句块组成的主体。 这个例子使用下面的expression式:

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

    如果指定单个expression式,则Java运行时将评估expression式,然后返回其值。 或者,您可以使用返回语句:

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

    返回语句不是一个expression式; 在lambdaexpression式中,必须将括号括在括号({})中。 但是,您不必在大括号中包含void方法调用。 例如,以下是有效的lambdaexpression式:

     email -> System.out.println(email) 

请注意,lambdaexpression式看起来很像一个方法声明; 您可以将lambdaexpression式视为匿名方法 – 没有名称的方法。


以下是如何使用lambdaexpression式“传递方法”:

 interface I { public void myMethod(Component component); } class A { public void changeColor(Component component) { // code here } public void changeSize(Component component) { // code here } } class B { public void setAllComponents(Component[] myComponentArray, I myMethodsInterface) { for(Component leaf : myComponentArray) { if(leaf instanceof Container) { // recursive call if Container Container node = (Container)leaf; setAllComponents(node.getComponents(), myMethodInterface); } // end if node myMethodsInterface.myMethod(leaf); } // end looping through components } } class C { A a = new A(); B b = new B(); public C() { b.setAllComponents(this.getComponents(), component -> a.changeColor(component)); b.setAllComponents(this.getComponents(), component -> a.changeSize(component)); } } 

使用java.lang.reflect.Method对象并调用invoke

首先用你想作为parameter passing的方法来定义一个接口

 public interface Callable { public void call(int param); } 

用这个方法来实现一个类

 class Test implements Callable { public void call(int param) { System.out.println( param ); } } 

//像这样调用

 Callable cmd = new Test(); 

这允许您将cmd作为parameter passing,并调用在接口中定义的方法调用

 public invoke( Callable callable ) { callable.call( 5 ); } 

尽pipe对于Java 7及以下版本来说,这还不是有效的,但我相信我们应该outlook未来,至less认识到Java 8等新版本的变化 。

也就是说,这个新版本带来了Java的lambdas和方法引用(以及新的API ,这是另一个有效的解决scheme,虽然它们仍然需要一个接口,但是没有创build新的对象,额外的类文件不需要污染输出目录由JVM处理。

这两种风格(lambda和方法引用)都要求使用一个使用其签名的方法的接口:

 public interface NewVersionTest{ String returnAString(Object oIn, String str); } 

方法的名称从这里不重要。 在接受lambda的情况下,方法引用也是如此。 例如,在这里使用我们的签名:

 public static void printOutput(NewVersionTest t, Object o, String s){ System.out.println(t.returnAString(o, s)); } 

这只是一个简单的接口调用,直到lambda 1通过:

 public static void main(String[] args){ printOutput( (Object oIn, String sIn) -> { System.out.println("Lambda reached!"); return "lambda return"; } ); } 

这将输出:

 Lambda reached! lambda return 

方法的参考是相似的。 鉴于:

 public class HelperClass{ public static String testOtherSig(Object o, String s){ return "real static method"; } } 

主要是:

 public static void main(String[] args){ printOutput(HelperClass::testOtherSig); } 

输出将是real static method 。 方法引用可以是静态的,实例,任意实例的非静态的,甚至是构造函数 。 对于构造函数,会使用类似于ClassName::new东西。

1这不被认为是一个lambda,因为它有副作用。 但是,它确实说明了使用一种更直接的可视化方式。

上次我查了一下,Java不能自己做你想做的事情, 你必须使用“解决方法”来解决这些限制。 据我所知,接口是一种替代scheme,但不是一个好的select。 也许谁告诉你这个意思是这样的:

 public interface ComponentMethod { public abstract void PerfromMethod(Container c); } public class ChangeColor implements ComponentMethod { @Override public void PerfromMethod(Container c) { // do color change stuff } } public class ChangeSize implements ComponentMethod { @Override public void PerfromMethod(Container c) { // do color change stuff } } public void setAllComponents(Component[] myComponentArray, ComponentMethod myMethod) { for (Component leaf : myComponentArray) { if (leaf instanceof Container) { //recursive call if Container Container node = (Container) leaf; setAllComponents(node.getComponents(), myMethod); } //end if node myMethod.PerfromMethod(leaf); } //end looping through components } 

然后你会调用:

 setAllComponents(this.getComponents(), new ChangeColor()); setAllComponents(this.getComponents(), new ChangeSize()); 

如果你不需要这些方法来返回一些东西,你可以让它们返回Runnable对象。

 private Runnable methodName (final int arg){ return new Runnable(){ public void run(){ // do stuff with arg } } } 

然后像这样使用它:

 private void otherMethodName (Runnable arg){ arg.run(); } 

使用观察者模式(有时也称为监听者模式):

 interface ComponentDelegate { void doSomething(Component component); } public void setAllComponents(Component[] myComponentArray, ComponentDelegate delegate) { // ... delegate.doSomething(leaf); } setAllComponents(this.getComponents(), new ComponentDelegate() { void doSomething(Component component) { changeColor(component); // or do directly what you want } }); 

new ComponentDelegate()...声明一个实现接口的匿名types。

Java有一个机制来传递名称和调用它。 这是反思机制的一部分。 您的函数应该采用类Method的附加参数。

 public void YouMethod(..... Method methodToCall, Object objWithAllMethodsToBeCalled) { ... Object retobj = methodToCall.invoke(objWithAllMethodsToBeCalled, arglist); ... } 

这里是一个基本的例子:

 public class TestMethodPassing { private static void println() { System.out.println("Do println"); } private static void print() { System.out.print("Do print"); } private static void performTask(BasicFunctionalInterface functionalInterface) { functionalInterface.performTask(); } @FunctionalInterface interface BasicFunctionalInterface { void performTask(); } public static void main(String[] arguments) { performTask(TestMethodPassing::println); performTask(TestMethodPassing::print); } } 

输出:

 Do println Do print 

我不是一个Java专家,但我解决你的问题是这样的:

 @FunctionalInterface public interface AutoCompleteCallable<T> { String call(T model) throws Exception; } 

我在我的特殊界面中定义参数

 public <T> void initialize(List<T> entries, AutoCompleteCallable getSearchText) {....... //call here String value = getSearchText.call(item); ... } 

最后,我在调用initialize方法的时候实现了getSearchText方法。

 initialize(getMessageContactModelList(), new AutoCompleteCallable() { @Override public String call(Object model) throws Exception { return "custom string" + ((xxxModel)model.getTitle()); } }) 

从Java 8开始,有一个Function<T, R>接口( docs ),它有方法

 R apply(T t); 

您可以使用它将函数作为parameter passing给其他函数。 T是函数的inputtypes,R是返回types。

在你的例子中,你需要传递一个把Componenttypes作为input的函数,并且什么都不返回 – Void 。 在这种情况下, Function<T, R>不是最好的select,因为没有Voidtypes的自动装箱。 你正在寻找的接口被称为Consumer<T> ( docs )与方法

 void accept(T t); 

它看起来像这样:

 public void setAllComponents(Component[] myComponentArray, Consumer<Component> myMethod) { for (Component leaf : myComponentArray) { if (leaf instanceof Container) { Container node = (Container) leaf; setAllComponents(node.getComponents(), myMethod); } myMethod.accept(leaf); } } 

你可以使用方法引用来调用它:

 setAllComponents(this.getComponents(), this::changeColor); setAllComponents(this.getComponents(), this::changeSize); 

假设您已经在同一个类中定义了changeColor()和changeSize()方法。


如果您的方法碰巧接受多个参数,则可以使用BiFunction<T, U, R> – T和U作为input参数的types,R是返回types。 还有BiConsumer<T, U> (两个参数,没有返回types)。 不幸的是,对于3个以上的input参数,你必须自己创build一个接口。 例如:

 public interface Function4<A, B, C, D, R> { R apply(A a, B b, C c, D d); }