【问题标题】:How do I perform a JAVA callback between classes? [duplicate]如何在类之间执行 JAVA 回调? [复制]
【发布时间】:2013-08-19 05:07:28
【问题描述】:

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

Parent 类中的部分,显然不正确?

【问题讨论】:

标签: java callback


【解决方案1】:

定义一个接口,并在接收回调的类中实现它。

注意你的情况下的多线程。

来自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 来添加多个侦听器。在不需要的地方,只需一个参考即可获得更好的性能。

【讨论】:

  • 界面的意义何在?为什么CallBackImpl 不能单独作为一个类而不实现接口?
  • @Imray 通过使用接口,您可以灵活地向调用者注册任何实现 CallBack 的类。它不必只是 CallBackImpl。
  • 这不是单线程和阻塞的吗?来自不同线程的回调的示例是什么?
  • 最佳答案,像魅力一样工作:)
  • @WeishiZeng 是的,这是单线程。多线程与回调没有直接关系,解决方案取决于应用程序。
【解决方案2】:

使用观察者模式。它的工作原理是这样的:

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。这就是观察者模式的优势,它减少了依赖并增加了可重用性。

【讨论】:

  • 很好的解释,在所有答案和整个互联网上的基本示例中,我从你的解释中理解了回调的使用......很好的答案(Y)
  • 我不明白为什么要在 MyForm 的构造函数中创建 MyClass 的实例,因为当任何主体在其他第三类中创建 MyClass 的实例并调用 addListener 时,他将创建 MyForm 的实例或任何其他派生自 MyListener 的类。
  • 没有。在其他类中对 MyClass 的实例调用 addListener 不会导致 MyForm 的实例被实例化。我同意将 MyClass 注入 MyForm 的构造函数可能会更好,但我试图保持示例简单,并专注于 just 观察者模式。
  • 解释清楚,谢谢。
  • 干净的 Java 代码,正如人们所期望的那样简单。太棒了。
【解决方案3】:

在这种特殊情况下,以下应该有效:

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

它是一个匿名子类。

【讨论】:

    【解决方案4】:

    我不知道这是否是您正在寻找的,但您可以通过将回调传递给子类来实现这一点。

    首先定义一个通用回调:

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

    在 ServerConnections 实例化上创建一个新的 ITypedCallback 实例:

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

    在回调对象上调用执行方法。

    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%正确,直接在这里编码。

    【讨论】:

    • 如果它可以通过声明的接口方法实现默认语义,这样代码将更具可读性,并且易于维护?
    • 对不起,我不明白你的问题。你的意思是,声明一个回调实现而不是直接实例化回调接口?
    • 是的,无论如何,这是见仁见智的问题。
    • 我正在寻找通用回调,谢谢
    【解决方案5】:

    IMO,你应该看看Observer Pattern,这就是大多数听众的工作方式

    【讨论】:

      猜你喜欢
      • 2012-11-06
      • 1970-01-01
      • 1970-01-01
      • 2019-11-07
      • 2016-01-01
      • 1970-01-01
      • 2018-09-05
      • 2010-12-01
      • 2021-02-15
      相关资源
      最近更新 更多