Функции обратного вызова в Java
Есть ли способ передать функцию обратного вызова в методе Java?
Поведение, которое я пытаюсь имитировать, - передать делегат .Net делегату функции.
Я видел людей, предлагающих создать отдельный объект, но это кажется излишним, однако я знаю, что иногда overkill - единственный способ сделать что-то.
Ответы
Ответ 1
Если вы имеете в виду что-то вроде анонимного делегата .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));
}
}
Ответ 2
Вы можете использовать интерфейс. См. Внедрить процедуры обратного вызова в Java из JavaWorld.
Ответ 3
Начиная с 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.
Ответ 4
Для простоты вы можете использовать Runnable:
private void runCallback(Runnable callback)
{
// Run callback
callback.run();
}
Использование:
runCallback(new Runnable()
{
@Override
public void run()
{
// Running callback
}
});
Ответ 5
Немного nitpicking:
Мне кажется, люди, предлагающие создать отдельный объект, но это кажется излишество
Передача обратного вызова включает создание отдельного объекта практически в любом языке OO, поэтому его вряд ли можно считать излишним. То, что вы, вероятно, имеете в виду, это то, что в Java требуется создать отдельный класс, который является более подробным (и более ресурсоемким), чем на языках с явными первоклассными функциями или закрытием. Тем не менее, анонимные классы по меньшей мере уменьшают многословие и могут использоваться inline.
Ответ 6
все же я вижу, что есть наиболее предпочтительный способ, который был тем, что я искал... он в основном был получен из этих ответов, но мне пришлось манипулировать им, чтобы сделать его более излишним и эффективным... и я думаю, что все ищут то, что я придумаю
К точке::
сначала сделать интерфейс, который прост
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
- это функция, которая требует некоторого времени, когда вы хотите добавить к нему обратный вызов, чтобы уведомить вас о результатах, добавить интерфейс обратного вызова в качестве параметра этого метода
надеюсь, моя точка зрения ясна, наслаждайтесь;)
Ответ 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);
}
}
}
Ответ 8
Это очень просто в 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();
}
}
Ответ 9
Метод не является (пока) первоклассным объектом в Java; вы не можете передать указатель функции в качестве обратного вызова. Вместо этого создайте объект (который обычно реализует интерфейс), который содержит необходимый вам метод, и передайте его.
Предложения по закрытию в Java &mdash, которые предоставили бы поведение, которое вы ищете, были сделаны, но ни один из них не будет включен в предстоящую версию Java 7.
Ответ 10
Когда мне нужна такая функциональность в Java, я обычно использую шаблон наблюдателя. Это подразумевает дополнительный объект, но я думаю, что это чистый путь, и это широко известный шаблон, который помогает с читабельностью кода.
Ответ 11
Проверьте блокировки, как они были реализованы в библиотеке lambdaj. Они действительно имеют поведение, очень похожее на делегатов С#:
http://code.google.com/p/lambdaj/wiki/Closures
Ответ 12
Я попытался использовать 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 ();
}
}
Ответ 13
Вы также можете сделать Callback
с помощью шаблона Delegate
:
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);
}
}
Ответ 14
он немного стар, но тем не менее... Я нашел ответ Питера Уилкинсона приятным, за исключением того, что он не работает для примитивных типов, таких как int/Integer.
Проблема заключается в .getClass()
для parameters[i]
, который возвращает, например, java.lang.Integer
, который, с другой стороны, не будет корректно интерпретироваться с помощью getMethod(methodName,parameters[])
(ошибка Java)...
Я объединил его с предложением Даниэля Спивака (в его ответе на это); шаги к успеху включали: ловить NoSuchMethodException
→ getMethods()
→ поиск соответствия по method.getName()
→ , а затем явным образом прокручивать список параметров и применять решение Daniels, такие как идентификация совпадений и совпадений подписи.
Ответ 15
Недавно я начал делать что-то вроде этого:
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);
}
}
Ответ 16
Я думаю, что использование абстрактного класса более элегантно, например:
// 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
*/
Ответ 17
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 это интерфейс, а не класс), а затем выполните его с помощью защиты самих интерфейсных методов. (* Это предопределение на самом деле является анонимным классом).
Затем вы получите ссылку на объект, через которую вы можете вызвать сам метод.
Ответ 18
Создайте интерфейс и создайте то же свойство интерфейса в классе обратного вызова.
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);
}
}