web-dev-qa-db-fra.com

Comment effectuer un callback Java entre des classes?

Je viens de JavaScript, dans lequel les rappels sont assez faciles. J'essaie de les implémenter dans Java, sans succès.

J'ai une classe de 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...");
    }
}

Ensuite, j'ai une classe enfant, appelée par le parent:

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

    }
}

Quelle est la bonne façon de mettre en œuvre le

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

partie, dans la classe des parents, qui est clairement pas correct?

59
Ágota Horváth

Définissez une interface et implémentez-la dans la classe qui recevra le rappel.

Ayez l'attention sur le multi-threading dans votre cas.

Exemple de code de 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);
    }
} 

Dans votre cas, à part le multi-threading, vous pouvez faire comme ceci:

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

Multi-threading

Rappelez-vous que cela ne gère pas le multi-threading, ceci est un autre sujet et peut avoir différentes solutions selon le projet.

Le motif observateur

L'observateur-modèle fait presque cela, la principale différence est l'utilisation d'un ArrayList pour ajouter plus d'un auditeur. Là où ce n'est pas nécessaire, vous obtenez de meilleures performances avec une seule référence.

114
Diego C Nascimento

Utilisez le motif observateur. Cela fonctionne comme ceci:

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

Vous créez une interface comportant une ou plusieurs méthodes à appeler lorsqu'un événement se produit. Ensuite, toute classe devant être notifiée lorsque des événements se produisent implémente cette interface.

Cela permet plus de flexibilité, car le producteur ne connaît que l’interface écouteur, pas une implémentation particulière de l’interface écouteur.

Dans mon exemple:

MyClass est le producteur ici comme notifiant une liste d'auditeurs.

MyListener est l'interface.

MyForm s'intéresse à quand somethingHappened, de sorte qu'il implémente MyListener et s'enregistre lui-même avec MyClass. Maintenant, MyClass peut informer MyForm des événements sans référencer directement MyForm. C'est la force du modèle d'observateur, cela réduit la dépendance et augmente la possibilité de réutilisation.

56
William Morrison

OMI, vous devriez jeter un oeil au Observer Pattern , et voici comment la plupart des auditeurs travaillent

6
sanbhat

Je ne sais pas si c'est ce que vous recherchez, mais vous pouvez y parvenir en transmettant un rappel à la classe enfant.

d'abord définir un rappel générique:

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

créez une nouvelle instance ITypedCallback sur l'instanciation ServerConnections:

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

appelez la méthode execute sur l'objet de rappel.

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

au fait: je n'ai pas vérifié si c'est correct à 100%, je l'ai codé directement ici.

3
pichsenmeister

Dans ce cas particulier, ce qui suit devrait fonctionner:

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

C'est une sous-classe anonyme.

0
akkerman