Creating callback functions in Java3 min read
If you are familiar with some event-driven languages such as JavaScript, especially the ES6 version or later, you probably have done some work with callback functions all the time. By all means, callback functions are defined as regular functions, but unlike regular functions, the invocation of callback functions is usually determined when an event get fired. For example, you have a client API that implements the Websocket behavior, typically you have to overwrite the onOpen
method. When the connection is opened (an event fired), your code inside the onOpen
method will get executed. In Java, we can create callback functions with the help of interfaces. Let’s look at the code example to see what I mean:
interface WebSocketEvent {
void onOpen();
void onClose();
}
class WebSocketEventHandler implements WebSocketEvent {
@Override
public void onOpen() {
System.out.println("Open websocket connection...");
}
@Override
public void onClose() {
System.out.println("Close websocket connection...");
}
}
class WebSocketEventGenerator {
public void handleWebSocketOpen(WebSocketEvent websocket) throws InterruptedException {
Thread.sleep(2000);
websocket.onOpen();
}
public void handleWebSocketClose(WebSocketEvent websocket) throws InterruptedException {
Thread.sleep(2000);
websocket.onClose();
}
}
public class Main {
public static void main(String[] args) throws InterruptedException {
WebSocketEvent websocket = new WebSocketEventHandler();
WebSocketEventGenerator webSocketEventGenerator = new WebSocketEventGenerator();
webSocketEventGenerator.handleWebSocketOpen(websocket);
System.out.println("Some work in between...");
webSocketEventGenerator.handleWebSocketClose(websocket);
}
}
The first question is why we have to create an interface instead of a regular class? This is because, with an interface, you can define a general contract and you know which callback methods you need to override. In our example above, we create a simple interface with 2 methods onOpen
and onClose
. Right below it, we define a class that overrides these 2 callback methods. Then we have the WebSocketEventGenerator
where we simulate the initialization of event, here we have a simple event when a thread sleeps for a few seconds and then we invoke the onOpen
and onClose
methods on WebSocketEvent
instance. At the bottom, we have the Main
class to test out our example.
Let’s move on to another example:
interface Greeting {
void greet();
}
class EnglishGreeting implements Greeting {
@Override
public void greet() {
System.out.println("Hello!");
}
}
class VietnameseGreeting implements Greeting {
@Override
public void greet() {
System.out.println("Xin chào!");
}
}
class JapaneseGreeting implements Greeting {
@Override
public void greet() {
System.out.println("こんにちは!");
}
}
class GreetingEvent {
Greeting greeting;
public void registerGreetingHandler(Greeting greeting) {
this.greeting = greeting;
}
public void greeting() {
// presume some event happened here...
greeting.greet();
}
}
class Main {
public static void main(String[] args) {
Greeting vietnamese = new VietnameseGreeting();
Greeting english = new EnglishGreeting();
Greeting japanese = new JapaneseGreeting();
GreetingEvent greetingEvent = new GreetingEvent();
greetingEvent.registerGreetingHandler(vietnamese);
greetingEvent.greeting();
greetingEvent.registerGreetingHandler(english);
greetingEvent.greeting();
greetingEvent.registerGreetingHandler(japanese);
greetingEvent.greeting();
}
}
In this second example, we can clearly see that there are some components usually used together with callback functions:
- Event source (the source that initiates the event)
- Event listeners (listeners that do some action when an event happened)
- Callback methods (called when an event get fired)
First, we have created an interface with only one callback method greet()
that acts as an event listener. Then we greet
by different languages by providing 3 concrete classes. Next, we create a class GreetingEvent
which acts as an event source where we can register an event listener; actually, when we look at this class, we call the greet
method on the event listener that we have registered, but let’s presume some events actually happened and then we call the greet()
method. Then finally, at the bottom, we have a class to test out our greeting
example.