Funkcje zwrotne w Javie
Czy istnieje sposób na przekazanie funkcji call back w metodzie Java?
Zachowanie, które próbuję naśladować, to Delegat. Net przekazywany do funkcji.
Widziałem ludzi sugerujących stworzenie osobnego obiektu, ale wydaje się to przesadą, jednak zdaję sobie sprawę, że czasami przesada jest jedynym sposobem na robienie rzeczy.
16 answers
Jeśli masz na myśli coś takiego jak. NET anonymous delegate, myślę, że można również użyć anonimowej klasy Javy.
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));
}
}
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-12-17 12:59:31
Od Javy 8 istnieją odwołania do lambda i metody:
Na przykład, zdefiniujmy:
public class FirstClass {
String prefix;
public FirstClass(String prefix){
this.prefix = prefix;
}
public String addPrefix(String suffix){
return prefix +":"+suffix;
}
}
I
import java.util.function.Function;
public class SecondClass {
public String applyFunction(String name, Function<String,String> function){
return function.apply(name);
}
}
Wtedy możesz zrobić:
FirstClass first = new FirstClass("first");
SecondClass second = new SecondClass();
System.out.println(second.applyFunction("second",first::addPrefix));
Możesz znaleźć przykład na GitHubie, tutaj: julien-diener / MethodReference .
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-11-29 20:30:00
Dla uproszczenia, możesz użyć Runnable :
private void runCallback(Runnable callback)
{
// Run callback
callback.run();
}
Użycie:
runCallback(new Runnable()
{
@Override
public void run()
{
// Running callback
}
});
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-12-13 16:41:00
Trochę nitpicking:
Wydaje mi się, że ludzie sugerują stworzenie osobny obiekt, ale wydaje się, że overkill
Przekazywanie wywołania zwrotnego obejmuje tworzenie osobnego obiektu w prawie każdym języku OO, więc trudno go uznać za przesadę. Prawdopodobnie chodzi Ci o to, że w Javie wymaga to utworzenia osobnej klasy, która jest bardziej wyrazista (i bardziej zasobochłonna) niż w językach z jawnymi funkcjami pierwszej klasy lub zamknięciami. Jednak anonimowy klasy co najmniej zmniejszają słowność i mogą być używane w linii.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-06-22 10:13:57
Jednak widzę, że jest najbardziej preferowany sposób, który był tym, czego szukałem.. to w zasadzie pochodzi z tych odpowiedzi, ale musiałem manipulować nim, aby był bardziej zbędny i wydajny.. i myślę, że każdy szuka tego, co wymyślę
Do punktu:
Najpierw stwórz interfejs tak prosty
public interface myCallback {
void onSuccess();
void onError(String err);
}
Teraz, aby to callback uruchomić, gdy kiedykolwiek chcesz zrobić, aby obsłużyć wyniki - bardziej prawdopodobne po asynchronicznym wywołaniu i chcesz uruchomić kilka rzeczy, które zależą od tych reusltów
// 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
czy funkcja, która zajmuje trochę czasu, chcesz dodać do niej callback, aby powiadomić cię, gdy pojawiły się wyniki, Dodaj interfejs oddzwaniania jako parametr do tej metody
Mam nadzieję, że mój punkt widzenia jest jasny, enjoy;)
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-08-28 08:47:52
Pomysł wdrożenia za pomocą biblioteki reflect był dla mnie interesujący i wpadł na to pomysł, który moim zdaniem działa całkiem dobrze. Jedyną wadą jest utrata sprawdzania czasu kompilacji, czy przekazujesz poprawne parametry.
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;
}
}
Używasz go w ten sposób
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);
}
}
}
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-07-15 04:18:47
Metoda nie jest (jeszcze) obiektem pierwszej klasy w Javie; nie można przekazać wskaźnika funkcji jako wywołania zwrotnego. Zamiast tego utwórz obiekt (który zazwyczaj implementuje interfejs) zawierający potrzebną metodę i przekaż ją.
Propozycje zamknięć w Javie-które zapewniłyby zachowanie, którego szukasz-zostały złożone, ale żadna z nich nie zostanie uwzględniona w nadchodzącym wydaniu Javy 7.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-01-14 16:48:53
Kiedy potrzebuję tego rodzaju funkcjonalności w Javie, zwykle używam wzorca obserwatora . Oznacza to dodatkowy obiekt, ale myślę, że jest to czysty sposób, i jest szeroko rozumianym wzorem, który pomaga w czytelności kodu.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-01-14 16:50:49
Sprawdź jak zostały one zaimplementowane w bibliotece lambdaj. W rzeczywistości mają zachowanie bardzo podobne do C# delegatów:
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-09-12 13:32:51
Jest to bardzo proste w Javie 8 z lambda.
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();
}
}
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-04-03 19:27:03
Próbowałem używać Javy.lang.zastanów się, aby zaimplementować 'callback', oto przykład:
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 ();
}
}
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-02-17 18:54:51
Możesz również wykonać {[3] } używając wzoru Delegate
:
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);
}
}
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-07-06 20:47:24
Jest trochę stary, ale mimo to... Znalazłem odpowiedź Peter Wilkinson ładny z wyjątkiem faktu, że to nie działa dla prymitywnych typów, takich jak int / Integer.
Problemem jest .getClass()
dla parameters[i]
, który zwraca na przykład java.lang.Integer
, który z drugiej strony nie będzie poprawnie interpretowany przez getMethod(methodName,parameters[])
(Błąd Javy) ...
Połączyłem to z sugestią Daniela śpiewaka (w jego odpowiedzi na to ); kroki do sukcesu obejmowały: łapanie NoSuchMethodException
-> getMethods()
-> Szukam dopasowanie jednego przez method.getName()
->, a następnie jawne zapętlenie listy parametrów i zastosowanie rozwiązania Danielsa, np. określenie dopasowania typu i dopasowania podpisu.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 12:34:42
Myślę, że używanie klasy abstrakcyjnej jest bardziej eleganckie, jak to:
// 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
*/
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-01-20 23:44:49
Ostatnio zacząłem robić coś takiego:
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);
}
}
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-10-14 08:03:02
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"
W zasadzie Jeśli chcesz, aby obiekt interfejsu był niemożliwe, ponieważ interfejs nie może zawierać obiektów.
Opcja jest taka, aby jakaś Klasa zaimplementowała interfejs, a następnie wywołała tę funkcję używając obiektu tej klasy. Ale to podejście jest naprawdę gadatliwe.
Alternatywnie, napisz new HelloWorld () (*oberserve to jest interfejs, a nie klasa), a następnie wykonaj go z definacją samych metod interfejsu. (*Ta definacja jest w rzeczywistości Klasa anonimowa). Następnie otrzymasz odniesienie do obiektu, za pomocą którego możesz wywołać samą metodę.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-10-20 02:47:17