Java中的回调函数

是否有一种方法可以在Java方法中传递回调函数?

我试图模拟的行为是一个。net委托被传递给一个函数。

我看到有人建议创建一个单独的对象,但这似乎有点过分,但我意识到有时过度是做事的唯一方法。

301917 次浏览

如果你指的是。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));


}
}

方法在Java中还不是一级对象;不能将函数指针作为回调函数传递。相反,创建一个对象(通常实现一个接口),其中包含您需要的方法并传递该方法。

Java中关于闭包的建议已经提出,它将提供您正在寻找的行为,但在即将发布的Java 7中不会包含任何闭包。

当我在Java中需要这种功能时,我通常使用观察者模式。它确实意味着一个额外的对象,但我认为这是一种干净的方式,而且是一种被广泛理解的模式,有助于提高代码的可读性。

有点吹毛求疵:

我听到有人建议创建一个 分开的物体,但看起来 过度< / p >

传递回调包括在几乎所有的OO语言中创建一个单独的对象,所以它几乎不能被认为是多余的。您的意思可能是,在Java中,它要求您创建一个单独的类,这比具有显式的一级函数或闭包的语言更冗长(也更消耗资源)。然而,匿名类至少减少了冗长,并且可以内联使用。

检查闭包是如何在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 ();
}
}

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

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

它有点老了,但无论如何…我发现Peter Wilkinson的答案很好,除了它不适用于像int/Integer这样的原始类型。 问题是parameters[i].getClass(),返回例如java.lang.Integer,另一方面,getMethod(methodName,parameters[]) (Java的错误)不会正确地解释…

我结合了Daniel Spiewak的建议(在他的回答中);成功的步骤包括:捕捉NoSuchMethodException -> getMethods() ->通过method.getName() ->寻找匹配的,然后显式遍历参数列表并应用Daniels解决方案,例如识别类型匹配和签名匹配。

为了简单起见,你可以使用可运行的:

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

用法:

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

或者使用Java8 lambdas

runCallback(() -> {
// Running callback
});

从Java 8开始,有lambda和方法引用:

例如,如果你想要一个函数接口A -> B,你可以使用:

import java.util.function.Function;


public MyClass {
public static String applyFunction(String name, Function<String,String> function){
return function.apply(name);
}
}

你可以这样称呼它:

MyClass.applyFunction("42", str -> "the answer is: " + str);
// returns "the answer is: 42"

你也可以通过类方法。例如:

@Value // lombok
public class PrefixAppender {
private String prefix;


public String addPrefix(String suffix){
return prefix +":"+suffix;
}
}

然后你可以这样做:

PrefixAppender prefixAppender= new PrefixAppender("prefix");
MyClass.applyFunction("some text", prefixAppender::addPrefix);
// returns "prefix:some text"

请注意:

这里我使用了函数接口Function<A,B>,但包java.util.function中还有许多其他接口。最值得注意的是

  • Supplier: void -> A
  • Consumer: A -> void
  • BiConsumer: (A,B) -> void
  • Function: A -> B
  • BiFunction: (A,B) -> C

以及许多其他专门研究某些输入/输出类型的程序。然后,如果它没有提供你需要的,你可以创建自己的FunctionalInterface:

@FunctionalInterface
interface Function3<In1, In2, In3, Out> { // (In1,In2,In3) -> Out
public Out apply(In1 in1, In2 in2, In3 in3);
}

使用示例:

String computeAnswer(Function3<String, Integer, Integer, String> f){
return f.apply("6x9=", 6, 9);
}


computeAnswer((question, a, b) -> question + "42");
// "6*9=42"

你也可以用抛出异常来实现:

@FunctionalInterface
interface FallibleFunction<In, Out, Ex extends Exception> {
Out get(In input) throws Ex;
}
public <Ex extends IOException> String yo(FallibleFunction<Integer, String, Ex> f) throws Ex {
return f.get(42);
}
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这是一个接口而不是一个类),然后跟随它的接口方法本身的定义。(*这个定义实际上是匿名类)。 然后你得到对象引用,通过它你可以调用方法本身

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

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

你也可以使用Delegate模式来完成__abc0:

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中使用lambdas非常容易。

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

然而,我看到了我最喜欢的方式,这就是我一直在寻找的。它基本上是从这些答案中衍生出来的,但我必须将其操作得更冗余和有效。我想每个人都在期待我的创意

一针见血::

首先创建一个接口那么简单

public interface myCallback {
void onSuccess();
void onError(String err);
}

现在让这个回调在你想要处理结果时运行——更可能是在async调用之后,你想要运行一些依赖于这些结果的东西

// import the Interface class here


public class App {


public static void main(String[] args) {
// call your method
doSomething("list your Params", new myCallback(){
@Override
public void onSuccess() {
// no errors
System.out.println("Done");
}


@Override
public void onError(String err) {
// error happen
System.out.println(err);
}
});
}


private void doSomething(String param, // some params..
myCallback callback) {
// now call onSuccess whenever you want if results are ready
if(results_success)
callback.onSuccess();
else
callback.onError(someError);
}


}

doSomething是一个需要一些时间的函数,你想给它添加一个回调函数来通知你结果出来了,将回调接口作为参数添加到这个方法

希望我的观点是清楚的,享受吧;)

创建一个接口,并在回调类中创建相同的接口属性。

interface dataFetchDelegate {
void didFetchdata(String data);
}
//callback class
public class BackendManager{
public dataFetchDelegate Delegate;


public void getData() {
//Do something, Http calls/ Any other work
Delegate.didFetchdata("this is callbackdata");
}


}
现在在你想要回调的类中实现上面创建的接口。 同时传递回调类的this对象/引用
public class Main implements dataFetchDelegate
{
public static void main( String[] args )
{
new Main().getDatafromBackend();
}


public void getDatafromBackend() {
BackendManager inc = new BackendManager();
//Pass this object as reference.in this Scenario this is Main Object
inc.Delegate = this;
//make call
inc.getData();
}


//This method is called after task/Code Completion
public void didFetchdata(String callbackData) {
// TODO Auto-generated method stub
System.out.println(callbackData);
}
}

在Java 8中,这个任务很简单,如果你想在多线程场景中使用回调,你可以像下面这样做:

public  void methodA (int n, IntConsumer consumer) {
    

// create a thread
Thread t = new Thread(() -> {
// some time consuming operation
int result = IntStream.range(0, n).sum();
// after the result is ready do something with it.
consumer.accept(result);
});
t.start();
}

使用这个方法:

methodA(1000000, System.out::println);