Java 中的回调函数
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/443708/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Callback functions in Java
提问by Omar Kooheji
Is there a way to pass a call back function in a Java method?
有没有办法在 Java 方法中传递回调函数?
The behavior I'm trying to mimic is a .Net Delegate being passed to a function.
我试图模仿的行为是传递给函数的 .Net 委托。
I've seen people suggesting creating a separate object but that seems overkill, however I am aware that sometimes overkill is the only way to do things.
我见过有人建议创建一个单独的对象,但这似乎有点矫枉过正,但是我知道有时矫枉过正是做事的唯一方法。
采纳答案by Gant
If you mean somthing like .NET anonymous delegate, I think Java's anonymous class can be used as well.
如果您的意思是 .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));
}
}
回答by erickson
A method is not (yet) a first-class object in Java; you can't pass a function pointer as a callback. Instead, create an object (which usually implements an interface) that contains the method you need and pass that.
方法(还)不是 Java 中的一流对象;您不能将函数指针作为回调传递。相反,创建一个包含您需要的方法的对象(通常实现一个接口)并传递它。
Proposals for closures in Java—which would provide the behavior you are looking for—have been made, but none will be included in the upcoming Java 7 release.
已经提出了 Java 中的闭包提案——它将提供您正在寻找的行为——已经提出,但在即将发布的 Java 7 版本中将不包含任何提案。
回答by MattK
When I need this kind of functionality in Java, I usually use the Observer pattern. It does imply an extra object, but I think it's a clean way to go, and is a widely understood pattern, which helps with code readability.
当我在 Java 中需要这种功能时,我通常使用观察者模式。它确实意味着一个额外的对象,但我认为这是一种干净的方法,并且是一种被广泛理解的模式,有助于提高代码可读性。
回答by Michael Borgwardt
A little nitpicking:
一点吹毛求疵:
I've seem people suggesting creating a separate object but that seems overkill
我似乎有人建议创建一个单独的对象,但这似乎有点矫枉过正
Passing a callback includes creating a separate object in pretty much any OO language, so it can hardly be considered overkill. What you probably mean is that in Java, it requires you to create a separate class, which is more verbose (and more resource-intensive) than in languages with explicit first-class functions or closures. However, anonymous classes at least reduce the verbosity and can be used inline.
传递回调包括在几乎任何面向对象语言中创建一个单独的对象,因此它几乎不能被认为是矫枉过正。您可能的意思是,在 Java 中,它要求您创建一个单独的类,与具有显式一流函数或闭包的语言相比,该类更加冗长(并且资源密集型)。然而,匿名类至少减少了冗长并且可以内联使用。
回答by Mario Fusco
Check the closures how they have been implemented in the lambdaj library. They actually have a behavior very similar to C# delegates:
检查闭包是如何在 lambdaj 库中实现的。它们实际上具有与 C# 委托非常相似的行为:
回答by u404192
I tried using java.lang.reflect to implement 'callback', here's a sample:
我尝试使用 java.lang.reflect 来实现“回调”,这是一个示例:
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 ();
}
}
回答by Peter Wilkinson
I found the idea of implementing using the reflect library interesting and came up with this which I think works quite well. The only down side is losing the compile time check that you are passing valid parameters.
我发现使用反射库实现的想法很有趣,并提出了我认为效果很好的方法。唯一的缺点是丢失了您传递有效参数的编译时检查。
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;
}
}
You use it like this
你像这样使用它
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);
}
}
}
回答by monnoo
it's a bit old, but nevertheless... I found the answer of Peter Wilkinson nice except for the fact that it does not work for primitive types like int/Integer.
The problem is the .getClass()
for the parameters[i]
, which returns for instance java.lang.Integer
, which on the other hand will not be correctly interpreted by getMethod(methodName,parameters[])
(Java's fault) ...
它有点旧,但是……我发现 Peter Wilkinson 的答案很好,除了它不适用于 int/Integer 等原始类型。问题是.getClass()
for parameters[i]
,它返回例如java.lang.Integer
,另一方面,它不会被getMethod(methodName,parameters[])
(Java 的错误)正确解释......
I combined it with the suggestion of Daniel Spiewak (in his answer to this); steps to success included: catching NoSuchMethodException
-> getMethods()
-> looking for the matching one by method.getName()
-> and then explicitly looping through the list of parameters and applying Daniels solution, such identifying the type matches and the signature matches.
我将其与 Daniel Spiewak 的建议结合起来(在他对此的回答中);成功的步骤包括:捕获NoSuchMethodException
-> getMethods()
-> 逐个查找匹配项method.getName()
-> 然后显式循环参数列表并应用 Daniels 解决方案,例如识别类型匹配和签名匹配。
回答by cprcrack
For simplicity, you can use a Runnable:
为简单起见,您可以使用Runnable:
private void runCallback(Runnable callback)
{
// Run callback
callback.run();
}
Usage:
用法:
runCallback(new Runnable()
{
@Override
public void run()
{
// Running callback
}
});
回答by Juh_
Since Java 8, there are lambda and method references:
从 Java 8 开始,就有了 lambda 和方法引用:
For example, if you want a functional interface A -> B
such as:
例如,如果您想要一个功能界面,A -> B
例如:
import java.util.function.Function;
public MyClass {
public static String applyFunction(String name, Function<String,String> function){
return function.apply(name);
}
}
then you can call it like so
那么你可以这样称呼它
MyClass.applyFunction("42", str -> "the answer is: " + str);
// returns "the answer is: 42"
Also you can pass class method. Say you have:
您也可以传递类方法。说你有:
@Value // lombok
public class PrefixAppender {
private String prefix;
public String addPrefix(String suffix){
return prefix +":"+suffix;
}
}
Then you can do:
然后你可以这样做:
PrefixAppender prefixAppender= new PrefixAppender("prefix");
MyClass.applyFunction("some text", prefixAppender::addPrefix);
// returns "prefix:some text"
Note:
注意:
Here I used the functional interface Function<A,B>
, but there are many others in the package java.util.function
. Most notable ones are
这里我使用了功能接口Function<A,B>
,但包中还有很多其他接口java.util.function
。最引人注目的是
Supplier
:void -> A
Consumer
:A -> void
BiConsumer
:(A,B) -> void
Function
:A -> B
BiFunction
:(A,B) -> C
Supplier
:void -> A
Consumer
:A -> void
BiConsumer
:(A,B) -> void
Function
:A -> B
BiFunction
:(A,B) -> C
and many others that specialize on some of the input/output type. Then, if it doesn't provide the one you need, you can create your own functional interface like so:
以及许多其他专门研究某些输入/输出类型的人。然后,如果它没有提供您需要的接口,您可以像这样创建自己的功能接口:
@FunctionalInterface
interface Function3<In1, In2, In3, Out> { // (In1,In2,In3) -> Out
public Out apply(In1 in1, In2 in2, In3 in3);
}
Example of use:
使用示例:
String computeAnswer(Function3<String, Integer, Integer, String> f){
return f.apply("6x9=", 6, 9);
}
computeAnswer((question, a, b) -> question + "42");
// "6*9=42"