Java中的callback函数

有没有办法在Java方法中传递callback函数?

我试图模仿的行为是一个.NET代理被传递给一个函数。

我曾经看到有人build议创build一个单独的对象,但这看起来有点过分,但我知道,有时矫枉过正是做事的唯一方法。

如果你的意思是像.NET匿名委托,我认为Java的匿名类也可以使用。

public class Main { public interface Visitor{ int doJob(int a, int b); } public static void main(String[] args) { Visitor adder = new Visitor(){ public int doJob(int a, int b) { return a + b; } }; Visitor multiplier = new Visitor(){ public int doJob(int a, int b) { return a*b; } }; System.out.println(adder.doJob(10, 20)); System.out.println(multiplier.doJob(10, 20)); } } 

你可以使用一个接口。 请参阅从JavaWorld 实现Java中的callback例程

自Java 8以来,有lambda和方法引用:

  • Oracle文档:Lambdaexpression式
  • Oracle文档:方法参考

例如,让我们定义:

 public class FirstClass { String prefix; public FirstClass(String prefix){ this.prefix = prefix; } public String addPrefix(String suffix){ return prefix +":"+suffix; } } 

 import java.util.function.Function; public class SecondClass { public String applyFunction(String name, Function<String,String> function){ return function.apply(name); } } 

那你可以这样做:

 FirstClass first = new FirstClass("first"); SecondClass second = new SecondClass(); System.out.println(second.applyFunction("second",first::addPrefix)); 

你可以在github上find一个例子,在这里: julien-diener / MethodReference 。

为了简单起见,您可以使用Runnable

 private void runCallback(Runnable callback) { // Run callback callback.run(); } 

用法:

 runCallback(new Runnable() { @Override public void run() { // Running callback } }); 

有点挑剔:

我似乎有人build议创build一个单独的对象,但似乎矫枉过正

传递callback包括用几乎所有的OO语言创build一个单独的对象,所以它很难被视为矫枉过正。 你可能的意思是,在Java中,它需要你创build一个单独的类,它比具有显式一级函数或闭包的语言更加冗长(并且更耗费资源)。 不过,匿名类至less要减less冗长,可以内联使用。

我发现实现使用reflection库有趣的想法,想出了这个我认为工作得很好的。 唯一的缺点是失去了编译时间检查你传递有效的参数。

 public class CallBack { private String methodName; private Object scope; public CallBack(Object scope, String methodName) { this.methodName = methodName; this.scope = scope; } public Object invoke(Object... parameters) throws InvocationTargetException, IllegalAccessException, NoSuchMethodException { Method method = scope.getClass().getMethod(methodName, getParameterClasses(parameters)); return method.invoke(scope, parameters); } private Class[] getParameterClasses(Object... parameters) { Class[] classes = new Class[parameters.length]; for (int i=0; i < classes.length; i++) { classes[i] = parameters[i].getClass(); } return classes; } } 

你这样使用它

 public class CallBackTest { @Test public void testCallBack() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException { TestClass testClass = new TestClass(); CallBack callBack = new CallBack(testClass, "hello"); callBack.invoke(); callBack.invoke("Fred"); } public class TestClass { public void hello() { System.out.println("Hello World"); } public void hello(String name) { System.out.println("Hello " + name); } } } 

Java中还没有一个方法是第一类对象, 你不能传递一个函数指针作为callback。 相反,创build一个对象(通常实现一个接口),其中包含您需要的方法并通过该方法。

在Java中提供闭包的build议(这将提供您正在寻找的行为),但是即将到来的Java 7版本中都没有包含这些build议。

当我需要Java中的这种function时,我通常使用Observer模式 。 它确实意味着一个额外的对象,但我认为这是一个干净的方式去,是一个广为人知的模式,这有助于代码的可读性。

检查closures是如何在lambdaj库中实现的。 他们实际上有一个非常类似于C#代表的行为:

http://code.google.com/p/lambdaj/wiki/Closures

我尝试使用java.lang.reflect来实现“callback”,这里是一个示例:

 package StackOverflowQ443708_JavaCallBackTest; import java.lang.reflect.*; import java.util.concurrent.*; class MyTimer { ExecutorService EXE = //Executors.newCachedThreadPool (); Executors.newSingleThreadExecutor (); public static void PrintLine () { System.out.println ("--------------------------------------------------------------------------------"); } public void SetTimer (final int timeout, final Object obj, final String methodName, final Object... args) { SetTimer (timeout, obj, false, methodName, args); } public void SetTimer (final int timeout, final Object obj, final boolean isStatic, final String methodName, final Object... args) { Class<?>[] argTypes = null; if (args != null) { argTypes = new Class<?> [args.length]; for (int i=0; i<args.length; i++) { argTypes[i] = args[i].getClass (); } } SetTimer (timeout, obj, isStatic, methodName, argTypes, args); } public void SetTimer (final int timeout, final Object obj, final String methodName, final Class<?>[] argTypes, final Object... args) { SetTimer (timeout, obj, false, methodName, argTypes, args); } public void SetTimer (final int timeout, final Object obj, final boolean isStatic, final String methodName, final Class<?>[] argTypes, final Object... args) { EXE.execute ( new Runnable() { public void run () { Class<?> c; Method method; try { if (isStatic) c = (Class<?>)obj; else c = obj.getClass (); System.out.println ("Wait for " + timeout + " seconds to invoke " + c.getSimpleName () + "::[" + methodName + "]"); TimeUnit.SECONDS.sleep (timeout); System.out.println (); System.out.println ("invoking " + c.getSimpleName () + "::[" + methodName + "]..."); PrintLine (); method = c.getDeclaredMethod (methodName, argTypes); method.invoke (obj, args); } catch (Exception e) { e.printStackTrace(); } finally { PrintLine (); } } } ); } public void ShutdownTimer () { EXE.shutdown (); } } public class CallBackTest { public void onUserTimeout () { System.out.println ("onUserTimeout"); } public void onTestEnd () { System.out.println ("onTestEnd"); } public void NullParameterTest (String sParam, int iParam) { System.out.println ("NullParameterTest: String parameter=" + sParam + ", int parameter=" + iParam); } public static void main (String[] args) { CallBackTest test = new CallBackTest (); MyTimer timer = new MyTimer (); timer.SetTimer ((int)(Math.random ()*10), test, "onUserTimeout"); timer.SetTimer ((int)(Math.random ()*10), test, "onTestEnd"); timer.SetTimer ((int)(Math.random ()*10), test, "A-Method-Which-Is-Not-Exists"); // java.lang.NoSuchMethodException timer.SetTimer ((int)(Math.random ()*10), System.out, "println", "this is an argument of System.out.println() which is called by timer"); timer.SetTimer ((int)(Math.random ()*10), System.class, true, "currentTimeMillis"); timer.SetTimer ((int)(Math.random ()*10), System.class, true, "currentTimeMillis", "Should-Not-Pass-Arguments"); // java.lang.NoSuchMethodException timer.SetTimer ((int)(Math.random ()*10), String.class, true, "format", "%d %X", 100, 200); // java.lang.NoSuchMethodException timer.SetTimer ((int)(Math.random ()*10), String.class, true, "format", "%d %X", new Object[]{100, 200}); timer.SetTimer ((int)(Math.random ()*10), test, "NullParameterTest", new Class<?>[]{String.class, int.class}, null, 888); timer.ShutdownTimer (); } } 

您也可以使用Delegate模式进行Callback

Callback.java

 public interface Callback { void onItemSelected(int position); } 

PagerActivity.java

 public class PagerActivity implements Callback { CustomPagerAdapter mPagerAdapter; public PagerActivity() { mPagerAdapter = new CustomPagerAdapter(this); } @Override public void onItemSelected(int position) { // Do something System.out.println("Item " + postion + " selected") } } 

CustomPagerAdapter.java

 public class CustomPagerAdapter { private static final int DEFAULT_POSITION = 1; public CustomPagerAdapter(Callback callback) { callback.onItemSelected(DEFAULT_POSITION); } } 

在Java 8中,使用lambdaexpression式非常简单。

 public interface Callback { void callback(); } public class Main { public static void main(String[] args) { methodThatExpectsACallback(() -> System.out.println("I am the callback.")); } private static void methodThatExpectsACallback(Callback callback){ System.out.println("I am the method."); callback.callback(); } } 

这有点旧了,不过……我发现Peter Wilkinson的答案很好,除了它不适用于像int / Integer这样的基本types。 问题是parameters[i].getClass() ,它返回实例java.lang.Integer ,另一方面, getMethod(methodName,parameters[]) (Java的错误)将不会正确解释。 。

我把它与Daniel Spiewak的build议结合起来( 在他的回答中 )。 包括以下步骤:捕获NoSuchMethodException – > getMethods() – >通过method.getName() getMethods() – >查找匹配的一个,然后显式循环参数列表并应用Daniels解决scheme,例如识别types匹配和签名匹配。

我觉得使用抽象类更优雅,就像这样:

 // Something.java public abstract class Something { public abstract void test(); public void usingCallback() { System.out.println("This is before callback method"); test(); System.out.println("This is after callback method"); } } // CallbackTest.java public class CallbackTest extends Something { @Override public void test() { System.out.println("This is inside CallbackTest!"); } public static void main(String[] args) { CallbackTest myTest = new CallbackTest(); myTest.usingCallback(); } } /* Output: This is before callback method This is inside CallbackTest! This is after callback method */ 

我最近开始做这样的事情:

 public class Main { @FunctionalInterface public interface NotDotNetDelegate { int doSomething(int a, int b); } public static void main(String[] args) { // in java 8 (lambdas): System.out.println(functionThatTakesDelegate((a, b) -> {return a*b;} , 10, 20)); } public static int functionThatTakesDelegate(NotDotNetDelegate del, int a, int b) { // ... return del.doSomething(a, b); } } 
 public class HelloWorldAnonymousClasses { //this is an interface with only one method interface HelloWorld { public void printSomething(String something); } //this is a simple function called from main() public void sayHello() { //this is an object with interface reference followed by the definition of the interface itself new HelloWorld() { public void printSomething(String something) { System.out.println("Hello " + something); } }.printSomething("Abhi"); //imagine this as an object which is calling the function'printSomething()" } public static void main(String... args) { HelloWorldAnonymousClasses myApp = new HelloWorldAnonymousClasses(); myApp.sayHello(); } } //Output is "Hello Abhi" 

基本上如果你想使一个接口的对象是不可能的,因为接口不能有对象。

选项是让某个类实现接口,然后使用该类的对象调用该函数。 但是这种方法非常冗长。

或者,写新的HelloWorld()(* oberserve这是一个接口不是一个类),然后跟随它接口方法本身的定义。 (*这个定义实际上是匿名的类)。 然后你得到通过它可以调用方法本身的对象引用。