有没有办法在Java方法中传递回调函数?
我试图模仿的行为是传递给函数的.Net委托。
我见过人们建议创建一个单独的对象,但这看起来有点过分,但我知道有时矫枉过正是唯一的做法。
答案 0 :(得分:147)
如果您的意思是.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));
}
}
答案 1 :(得分:29)
从Java 8开始,有lambda和方法引用:
例如,让我们定义:
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上找到一个例子,在这里:julien-diener/MethodReference。
答案 2 :(得分:25)
为简单起见,您可以使用 Runnable :
private void runCallback(Runnable callback)
{
// Run callback
callback.run();
}
用法:
runCallback(new Runnable()
{
@Override
public void run()
{
// Running callback
}
});
答案 3 :(得分:16)
有点挑剔:
我似乎有人建议创建一个 看似单独的对象 矫枉过正
传递回调包括使用几乎任何OO语言创建单独的对象,因此很难将其视为过度杀伤。你可能意味着在Java中,它需要你创建一个单独的类,它比具有显式的第一类函数或闭包的语言更冗长(并且更加资源密集)。但是,匿名类至少可以减少冗长,并且可以内联使用。
答案 4 :(得分:11)
然而我看到最优选的方式是我正在寻找的...它基本上来自这些答案,但我不得不操纵它更多余和高效.. 我认为每个人都在寻找什么我想出了
首先使接口变得简单
public interface myCallback {
void onSuccess();
void onError(String err);
}
现在要在你希望处理结果的时候运行这个回调 - 更有可能在异步调用之后,你想运行一些依赖于这些重新连接的东西
// 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
是一个函数,需要花一些时间来为它添加回调以在结果出现时通知您,将回调接口添加为此方法的参数
答案 5 :(得分:7)
在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();
}
}
答案 6 :(得分:7)
我发现使用反射库实现的想法很有意思,并提出了我认为非常有效的方法。唯一的缺点是丢失编译时检查您是否传递了有效参数。
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);
}
}
}
答案 7 :(得分:5)
方法不是(还)Java中的第一类对象;你不能将函数指针作为回调传递。相反,创建一个包含所需方法的对象(通常实现一个接口)并传递它。
Java中的闭包建议 - 它将提供您正在寻找的行为 - 已经完成,但是没有一个将包含在即将发布的Java 7版本中。
答案 8 :(得分:4)
检查闭包是如何在lambdaj库中实现它们的。它们实际上的行为与C#委托非常相似:
答案 9 :(得分:4)
当我在Java中需要这种功能时,我通常使用Observer pattern。它确实意味着一个额外的对象,但我认为这是一个干净的方式,并且是一个广泛理解的模式,这有助于代码可读性。
答案 10 :(得分:3)
我尝试使用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 ();
}
}
答案 11 :(得分:3)
您也可以使用Workers
模式执行Callback
:
Callback.java
Delegate
PagerActivity.java
public interface Callback {
void onItemSelected(int position);
}
CustomPagerAdapter.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")
}
}
答案 12 :(得分:1)
它有点老了,但是......我发现Peter Wilkinson的答案很好,除了它不适用于像int / Integer这样的原始类型。
问题是.getClass()
的{{1}},它返回例如parameters[i]
,另一方面java.lang.Integer
将无法正确解释(Java的错误)......
我将其与Daniel Spiewak(in his answer to this)的建议结合起来;成功的步骤包括:捕捉getMethod(methodName,parameters[])
- &gt; NoSuchMethodException
- &gt;通过getMethods()
寻找匹配的一个 - &gt;然后显式循环遍历参数列表并应用Daniels解决方案,例如识别类型匹配和签名匹配。
答案 13 :(得分:1)
我最近开始做这样的事情:
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);
}
}
答案 14 :(得分:1)
使用 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);
答案 15 :(得分:0)
我认为使用抽象类更优雅,如下所示:
// 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
*/
答案 16 :(得分:0)
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这是一个接口而不是类),然后通过接口方法本身的定义来跟进它。 (*这种定义实际上是匿名类)。 然后你得到对象引用,通过它可以调用方法本身。
答案 17 :(得分:0)
创建一个接口,并在回调类中创建相同的接口属性。
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");
}
}
现在在您想被回调的类中,实现上面的Created Interface。 并且还传递您的类的“ 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);
}
}
答案 18 :(得分:-1)