2013-08-16 21 views
38

Pochodzę z JavaScript, w którym wywołania zwrotne są dość łatwe. Próbuję wdrożyć je do JAVA, bez powodzenia.Jak wykonać wywołanie zwrotne JAVA między klasami?

Mam klasy dominującej:

import java.net.Socket; 
import java.util.concurrent.ExecutorService; 
import java.util.concurrent.Executors; 

public class Server { 
    ExecutorService workers = Executors.newFixedThreadPool(10); 
    private ServerConnections serverConnectionHandler; 

    public Server(int _address) { 
     System.out.println("Starting Server..."); 
     serverConnectionHandler = new ServerConnections(_address); 

     serverConnectionHandler.newConnection = function(Socket _socket) { 
      System.out.println("A function of my child class was called."); 
     }; 

     workers.execute(serverConnectionHandler); 

     System.out.println("Do something else..."); 
    } 
} 

Następnie mam klasy dziecko, które nazywa się od rodzica:

import java.io.IOException; 
import java.net.ServerSocket; 
import java.net.Socket; 
import java.util.logging.Level; 
import java.util.logging.Logger; 

public class ServerConnections implements Runnable { 
    private int serverPort; 
    private ServerSocket mainSocket; 

    public ServerConnections(int _serverPort) { 
     serverPort = _serverPort; 
    } 

    @Override 
    public void run() { 
     System.out.println("Starting Server Thread..."); 

     try { 
      mainSocket = new ServerSocket(serverPort); 

      while (true) { 
       newConnection(mainSocket.accept()); 
      } 
     } catch (IOException ex) { 
      Logger.getLogger(Server.class.getName()).log(Level.SEVERE, null, ex); 
     } 
    } 

    public void newConnection(Socket _socket) { 

    } 
} 

Co jest właściwym sposobem realizacji

serverConnectionHandler.newConnection = function(Socket _socket) { 
    System.out.println("A function of my child class was called."); 
}; 

część, w klasie nadrzędnej, co oczywiście nie jest poprawne?

+1

Schemat polecenia. –

+0

Zobacz: http://stackoverflow.com/a/4685606/483588 –

Odpowiedz

81

zdefiniować interfejs i wdrożyć ją w klasie, która będzie odbierać wywołania zwrotnego.

Zwróć uwagę na wielowątkowość w twoim przypadku.

przykład

Kod z http://cleancodedevelopment-qualityseal.blogspot.com.br/2012/10/understanding-callbacks-with-java.html

interface CallBack {     //declare an interface with the callback methods, so you can use on more than one class and just refer to the interface 
    void methodToCallBack(); 
} 

class CallBackImpl implements CallBack {   //class that implements the method to callback defined in the interface 
    public void methodToCallBack() { 
     System.out.println("I've been called back"); 
    } 
} 

class Caller { 

    public void register(CallBack callback) { 
     callback.methodToCallBack(); 
    } 

    public static void main(String[] args) { 
     Caller caller = new Caller(); 
     CallBack callBack = new CallBackImpl();  //because of the interface, the type is Callback even thought the new instance is the CallBackImpl class. This alows to pass different types of classes that have the implementation of CallBack interface 
     caller.register(callBack); 
    } 
} 

w przypadku, oprócz wielowątkowości można zrobić tak:

interface ServerInterface { 
    void newSeverConnection(Socket socket); 
} 

public class Server implements ServerInterface { 

    public Server(int _address) { 
     System.out.println("Starting Server..."); 
     serverConnectionHandler = new ServerConnections(_address, this); 
     workers.execute(serverConnectionHandler); 
     System.out.println("Do something else..."); 
    } 

    void newServerConnection(Socket socket) { 
     System.out.println("A function of my child class was called."); 
    } 

} 

public class ServerConnections implements Runnable { 

    private ServerInterface serverInterface; 

    public ServerConnections(int _serverPort, ServerInterface _serverInterface) { 
     serverPort = _serverPort; 
     serverInterface = _serverInterface; 
    } 

    @Override 
    public void run() { 
     System.out.println("Starting Server Thread..."); 

     if (serverInterface == null) { 
      System.out.println("Server Thread error: callback null"); 
     } 

     try { 
      mainSocket = new ServerSocket(serverPort); 

      while (true) { 
       serverInterface.newServerConnection(mainSocket.accept()); 
      } 
     } catch (IOException ex) { 
      Logger.getLogger(Server.class.getName()).log(Level.SEVERE, null, ex); 
     } 
    } 
} 

wielowątkowości

Pamiętaj, to nie robi obsługuje wielowątkowość, jest to inny temat i może mieć różne rozwiązania w zależności od projektu.

Obserwator deseń

Obserwator deseń ma prawie to, główną różnicą jest użycie ArrayList na dodanie więcej niż jednego słuchacza. Tam, gdzie nie jest to potrzebne, uzyskujesz lepszą wydajność z jednym odniesieniem.

+2

Jaki jest sens interfejsu? Dlaczego 'CallBackImpl' nie może być klasą samą w sobie, bez implementowania interfejsu? – CodyBugstein

+9

@Imray Korzystając z interfejsu, możesz elastycznie rejestrować dowolną klasę, która implementuje CallBack z osobą dzwoniącą. To nie musi być po prostu CallBackImpl. – crysis

+0

Czy to nie będzie pojedynczy wątek i blokowanie? Jaki byłby przykład wywołań zwrotnych z innego wątku? –

43

Użyj wzorca obserwatora. Działa to tak:

interface MyListener{ 
    void somethingHappened(); 
} 

public class MyForm implements MyListener{ 
    MyClass myClass; 
    public MyForm(){ 
     this.myClass = new MyClass(); 
     myClass.addListener(this); 
    } 
    public void somethingHappened(){ 
     System.out.println("Called me!"); 
    } 
} 
public class MyClass{ 
    private List<MyListener> listeners = new ArrayList<MyListener>(); 

    public void addListener(MyListener listener) { 
     listeners.add(listener); 
    } 
    void notifySomethingHappened(){ 
     for(MyListener listener : listeners){ 
      listener.somethingHappened(); 
     } 
    } 
} 

Tworzysz interfejs, który ma jedną lub więcej metod do wywołania, gdy zdarzy się jakieś zdarzenie. Następnie każda klasa, która musi zostać powiadomiona o wystąpieniu zdarzenia, implementuje ten interfejs.

Pozwala to na większą elastyczność, ponieważ producent zna tylko interfejs odbiornika, , a nie określoną implementację interfejsu nasłuchującego.

W moim przykładzie:

MyClass jest producentem tutaj jako jej powiadamiania listę słuchaczy.

MyListener to interfejs.

MyForm jest zainteresowany somethingHappened, więc wdraża MyListener i rejestruje się pod numerem MyClass. Teraz MyClass może poinformować MyForm o zdarzeniach bez bezpośredniego odwoływania się do MyForm. Jest to siła wzorca obserwatora, zmniejsza zależność i zwiększa możliwość ponownego użycia.

+3

ładne wyjaśnienie, ze wszystkich odpowiedzi i tego podstawowego przykładu, który jest w całym Internecie, zrozumiałem użycie wywołań zwrotnych z twojego wyjaśnienia .. wspaniała odpowiedź (Y) – Bawa

+0

Nie rozumiem jednej rzeczy, dlaczego tworzysz instancję MyClass wewnątrz konstruktora MyForm, ponieważ gdy dowolny obiekt tworzy instancję klasy MyClass w innej trzeciej klasie i wywoła metodę addListener, utworzy instancję MyForm lub dowolną inną klasę pochodną od MyListener. –

+0

Nie. CalLing addListener na instancji klasy MyClass w innych klasach nie spowoduje utworzenia wystąpienia instancji MyForm. Zgadzam się, że może być lepiej, aby MyClass był wstrzykiwany w konstruktorze MyForm, ale starałem się, aby przykład był prosty i skupiony na * tylko * wzorca obserwatora. –

3

Nie wiem, czy tego właśnie szukasz, ale możesz to osiągnąć, przekazując oddzwonienie do klasy dziecka.

najpierw zdefiniować rodzajowe callback:

public interface ITypedCallback<T> { 
    void execute(T type); 
} 

utworzyć nową instancję ITypedCallback na ServerConnections instancji:

public Server(int _address) { 
    serverConnectionHandler = new ServerConnections(new ITypedCallback<Socket>() { 
     @Override 
     public void execute(Socket socket) { 
      // do something with your socket here 
     } 
    }); 
} 

Wywołać Methode wykonać na obiekcie zwrotnego.

public class ServerConnections implements Runnable { 

    private ITypedCallback<Socket> callback; 

    public ServerConnections(ITypedCallback<Socket> _callback) { 
     callback = _callback; 
    } 

    @Override 
    public void run() { 
     try { 
      mainSocket = new ServerSocket(serverPort); 
      while (true) { 
       callback.execute(mainSocket.accept()); 
      } 
     } catch (IOException ex) { 
      Logger.getLogger(Server.class.getName()).log(Level.SEVERE, null, ex); 
     } 
    } 
} 

btw: Nie sprawdziłem, czy jest w 100% poprawny, bezpośrednio zakodowałem tutaj.

+0

Jak to zrobić, jeśli może zaimplementować domyślną semantyczną z zadeklarowanymi metodami interfejsu, aby kod był bardziej czytelny i łatwy w utrzymaniu? –

+0

Przepraszam, ale nie dostaję twojego pytania. masz na myśli deklarowanie implementacji wywołania zwrotnego zamiast bezpośredniego tworzenia interfejsu wywołań zwrotnych? – 3x14159265

+0

Tak, tak, to jest kwestia opinii. –

0

W tym konkretnym przypadku, dodaje powinno działać:

serverConnectionHandler = new ServerConnections(_address) { 
    public void newConnection(Socket _socket) { 
     System.out.println("A function of my child class was called."); 
    } 
}; 

To anonimowe podklasy.

Powiązane problemy