Функции обратного вызова в Java
есть ли способ передать функцию обратного вызова в методе Java?
поведение, которое я пытаюсь имитировать, - это делегат .Net, передаваемый функции.
Я видел людей, предлагающих создать отдельный объект, но это кажется излишним, однако я знаю, что иногда излишество-это единственный способ сделать что-то.
16 ответов:
Если вы имеете в виду что-то вроде анонимного делегата .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 8, есть ссылки на лямбда и метод:
например, определим:
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.
для простоты, вы можете использовать Runnable:
private void runCallback(Runnable callback) { // Run callback callback.run(); }использование:
runCallback(new Runnable() { @Override public void run() { // Running callback } });
немного придирки:
Мне кажется, люди предлагают создать отдельный объект, но это, кажется, перебор
передача обратного вызова включает в себя создание отдельного объекта практически на любом языке OO, поэтому его вряд ли можно считать излишним. Вероятно, вы имеете в виду, что в Java требуется создать отдельный класс, который является более подробным (и более ресурсоемким), чем в языках с явными функциями первого класса или замыканиями. Однако анонимные классы, по крайней мере, уменьшают многословие и могут использоваться встраиваемыми.
но я вижу, что есть наиболее предпочтительный способ, который был то, что я искал.. он в основном получен из этих ответов, но мне пришлось манипулировать им для более избыточного и эффективного.. и я думаю, что все ищут то, что я придумал
к делу::
сначала сделать интерфейс это просто
public interface myCallback { void onSuccess(); void onError(String err); }Теперь, чтобы сделать этот обратный вызов выполнить, когда вы хотите сделать, чтобы обработать результаты -больше шансов после асинхронного вызова и вы хотите запустить некоторые вещи, которые зависят от этих reuslts
// 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это функция, которая занимает некоторое время вы хотите добавить обратный вызов к нему, чтобы уведомить вас, когда пришли результаты, добавить интерфейс обратного вызова в качестве параметра для этого методанадеюсь, что моя точка зрения ясна, наслаждайтесь;)
Я нашел идею реализации с использованием библиотеки reflect интересной и придумал это, что я думаю, работает довольно хорошо. Единственным недостатком является потеря проверки времени компиляции, что вы передаете допустимые параметры.
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; вы не можете передать указатель функции в качестве обратного вызова. Вместо этого создайте объект (который обычно реализует интерфейс), содержащий нужный вам метод, и передайте его.
предложения по закрытию в Java, которые обеспечивали бы поведение, которое вы ищете, были сделаны, но никто не будет включен в предстоящий выпуск Java 7.
когда мне нужна такая функциональность в Java, я обычно использую шаблон Observer. Это подразумевает дополнительный объект, но я думаю, что это чистый путь, и это широко понимаемый шаблон, который помогает с удобочитаемостью кода.
Проверьте замыкания, как они были реализованы в библиотеке lambdaj. На самом деле они ведут себя очень похоже на делегатов C#:
Это очень легко в Java 8 с лямбдами.
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(); } }
Я пробовал использовать java.ленг.подумайте, чтобы реализовать "обратный вызов", вот пример:
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 (); } }
вы также можете сделать
CallbackС помощьюDelegateшаблон:обратный.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); } }
это немного старый, но тем не менее... Я нашел ответ Питера Уилкинсона хорошим, за исключением того, что он не работает для примитивных типов, таких как int/Integer. Проблема заключается в
.getClass()наparameters[i], который возвращает, напримерjava.lang.Integer, который, с другой стороны, не будет правильно интерпретированgetMethod(methodName,parameters[])(ошибка Java)...Я объединил его с предложением Даниила Спивака (в ответ на это); шаги к успеху включено: ловить
NoSuchMethodException- >getMethods()- > ищет соответствующий один поmethod.getName()-> а затем явно перебираем список параметров и применяем решение Daniels, например идентифицируя совпадения типов и совпадения сигнатур.
Я думаю, что использование абстрактного класса более элегантно, например:
// 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"в основном, если вы хотите сделать объект интерфейса это невозможно, потому что интерфейс не может иметь объектов.
опция состоит в том, чтобы позволить некоторому классу реализовать интерфейс, а затем вызвать эту функцию, используя объект этого класса. Но этот подход действительно многословен.
в качестве альтернативы, напишите new HelloWorld () (*oberserve это интерфейс, а не класс), а затем выполните его с определением самих методов интерфейса. (*Этот дефиниция-это в действительности анонимный класс). Затем вы получаете ссылку на объект, через который вы можете вызвать сам метод.
Comments