2013-08-16 3 views
38

Я исхожу из JavaScript, в котором обратные вызовы довольно просты. Я пытаюсь реализовать их в JAVA без успеха.Как выполнить обратный вызов JAVA между классами?

У меня есть класс Parent:

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..."); 
    } 
} 

Тогда у меня есть дочерний класс, который вызывается из родителя:

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) { 

    } 
} 

Что такое правильный способ реализации

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

, в Родительском классе, что явно неверно?

+1

Шаблон команды. –

+0

См. Http://stackoverflow.com/a/4685606/483588 –

ответ

81

Определить интерфейс, и реализовать его в классе, который получит обратный вызов.

Обратите внимание на многопоточность в вашем корпусе.

пример

код из 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); 
    } 
} 

В вашем случае, кроме многопоточности вы могли бы сделать так:

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); 
     } 
    } 
} 

Многопоточность

Помните, что это не обрабатывать многопоточность, это еще одна тема и может иметь различные решения в зависимости от проекта.

Наблюдатель-паттерн

Наблюдатель-паттерн делает практически это, главным отличием является использование ArrayList для добавления более одного слушателя. Если это не требуется, вы получаете лучшую производительность с одной ссылкой.

+2

Какой смысл в интерфейсе? Почему «CallBackImpl» не может быть классом самостоятельно, без реализации интерфейса? – CodyBugstein

+9

@Imray Используя интерфейс, вы получаете возможность регистрировать любой класс, который реализует CallBack с помощью Caller. Это не должно быть просто CallBackImpl. – crysis

+0

Разве это не будет нитью и блокировкой? Что будет примером для обратных вызовов из разных потоков? –

6

ИМО, вы должны взглянуть на Observer Pattern, и это, как большинство слушателей работают

43

Используйте шаблон наблюдателя. Он работает следующим образом:

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(); 
     } 
    } 
} 

Вы создаете интерфейс, который имеет один или несколько методов для вызова при возникновении какого-либо события. Затем любой класс, который должен быть уведомлен о событиях, реализует этот интерфейс.

Это обеспечивает большую гибкость, поскольку производитель знает только о интерфейсе слушателя, не - особая реализация интерфейса слушателя.

В моем примере:

MyClass является производителем здесь его уведомления список слушателей.

MyListener - это интерфейс.

MyForm интересует, когда somethingHappened, поэтому он осуществляет MyListener и регистрируется с MyClass. Теперь MyClass может сообщить MyForm о событиях без прямого ссылки MyForm. Это сила шаблона наблюдателя, он уменьшает зависимость и увеличивает повторное использование.

+3

Хорошее объяснение, из всех ответов и того базового примера, что есть во всем Интернете, я понял использование обратных вызовов из вашего объяснения .. отличный ответ (Y) – Bawa

+0

Я не понимаю одну вещь, почему вы создаете экземпляр MyClass внутри конструктора MyForm, потому что, когда любое тело создает экземпляр MyClass в каком-то другом третьем классе и вызывает addListener, он создаст экземпляр MyForm или любого другого класса, полученного из MyListener. –

+0

Нет. CalLing addListener в экземпляре MyClass в других классах не будет создавать экземпляр экземпляра MyForm. Я согласен с тем, что MyClass может быть лучше встраиваться в конструктор MyForm, но я старался, чтобы этот пример был простым, и сосредоточился на * просто * шаблоне наблюдателя. –

3

Я не знаю, если это то, что вы ищете, но вы можете достичь этого, передав обратный вызов дочернему классу.

сначала определить общий обратный вызов:

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

создать новый экземпляр ITypedCallback на ServerConnections конкретизации:

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

вызов Выполнить METHODE на объекте обратного вызова.

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); 
     } 
    } 
} 

кстати: Я не проверял, соответствует ли он на 100%, напрямую закодировал его здесь.

+0

Как это, если он может реализовать семантику по умолчанию с помощью методов интерфейса, объявленных таким образом, что код будет более читабельным и прост в обслуживании? –

+0

извините, но у меня нет вашего вопроса. вы имеете в виду, объявляя реализацию обратного вызова вместо прямого создания интерфейса обратного вызова? – 3x14159265

+0

Да, во всяком случае, это вопрос мнения. –

0

В данном конкретном случае, следующий должен работать:

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

Это анонимный подкласс.

Смежные вопросы