Как мне выполнить обратный вызов JAVA между классами?
Я пришел из JavaScript, в котором обратные вызовы довольно просты. Я пытаюсь внедрить их в JAVA, но безуспешно.
У меня есть родительский класс:
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.");
};
часть, в родительском классе, что явно не правильно?
5 ответов
Определите интерфейс и реализуйте его в классе, который будет получать обратный вызов.
Обратите внимание на многопоточность в вашем случае.
Пример кода от 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
для добавления более одного слушателя. Там, где это не нужно, вы получаете лучшую производительность с одной ссылкой.
Используйте шаблон наблюдателя. Это работает так:
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
, Это сила модели наблюдателя, она уменьшает зависимость и увеличивает возможность повторного использования.
ИМО, вы должны взглянуть на шаблон наблюдателя, и именно так работает большинство слушателей
Я не знаю, ищите ли вы это, но вы можете достичь этого, передав обратный вызов в дочерний класс.
Сначала определите общий обратный вызов:
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
}
});
}
вызовите метод execute для объекта обратного вызова.
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% правильно, прямо закодировал это здесь.
В этом конкретном случае должно работать следующее:
serverConnectionHandler = new ServerConnections(_address) {
public void newConnection(Socket _socket) {
System.out.println("A function of my child class was called.");
}
};
Это анонимный подкласс.