Introduction
LinkSet is a tiny library created in order to relieve programmers from declaring listener interfaces. It utilizes Java 5 features and was designed to be a drop in replacement for a conventional “listener interface + anonymous class” solution.
Java is a great language, much cleaner than C#. But it lags behind in the area of listeners. Every time I did some programming in C#, I thought: "I wish Java had delegates." I also liked the Qt’s signal/slot approach. Some time ago, I thought: "Can I create something cleaner and easier to use than those syntactically horrible anonymous classes?" This way LinkSet (github.com/lbownik/linkset) was born. LinkSet is a little library that utilizes Java 5 features to provide a simple to use mechanism of listeners that doesn't require either listener interfaces, or anonymous classes.
Features of LinkSet
When I was designing LinkSet, I wanted it to be:
- Simple to use - the usage of a library should impose as little programming effort as possible. This means: no listener interfaces required and no special build steps needed.
- Flexible - both instance and static methods can be event handlers.
- Noninvasive – it does not require an event handling object to implement any particular interface and the event handling method can be
private
. - Easy to learn – it consists of only a few intuitive classes to use
- Small and reasonably fast
- Free - LGPL license
I hope that I met those requirements.
Working with LinkSet
To get started with LinkSet, you need to go to the project’s website (github.com/lbownik/linkset) and download the jar file. Then it is enough to include it into your project’s class path.
Event Source
LinkSet was designed to simplify the development of both event source and event handler classes. The following code presents a class that is a source of events.
This code shows how to implement a class that can be observed by listeners.
package com.mycompany.project1;
import org.linkset.DefaultListenerManager;
import org.linkset.ListenerManager;
import org.linkset.MethodPointer;
public class EventSource {
private final DefaultListenerManager clickListeners =
new DefaultListenerManager();
private MethodPointer vetoableListener;
public final static int LeftButton = 0;
public final static int RightButton = 1;
public ListenerManager clickdListeners() {
return this.clickListeners;
}
public void setVetoableListener(MethodPointer pointer) {
this.vetoableListener = pointer;
}
public void doStuff() throws Exception {
this.clickListeners.invokeAll(LeftButton);
final boolean canChangeState
(Boolean)this.vetoableListener.invoke();
if(canChangeState == true) {
}
}
}
The class declares two private
fields. The clickListeners
is an object of a class DefaultListenerManager
. This class is a collection of event handlers that should be invoked when an event occurs. The method clickListeners()
allows clients to connect its handlers via add(…)
method defined in ListenerManager
interface. This way a client can easily connect to an event source using the code like:
Source.clickListeners().add(….);
The second field called vetoableListener
is an object of a MethodPointer
class. This class implements an events handler method pointer that can be used when exactly one listener is required and its return value is necessary in following computations (like in a vetoable observer pattern). The method setVetoableListener(...)
allows a client to set the pointer reference. The method doStuff()
contains code that invokes all listeners with appropriate invokeAll(…)
and invoke(…)
methods.
Event Handler
The following code shows how to implement a class that provides methods that listen to events.
package com.mycompany.project1;
import org.linkset.HandlerMethod;
import org.linkset.MethodPointer;
public class EventHandler {
private EventSource provider = new EventSource();
public EventHandler() {
this.provider.clickdListeners().add(this, "clickListener");
this.provider.setVetoablePointer(new
MethodPointer(this.getClass(), "canChange"));
}
@HandlerMethod(id = "clickListener")
private void clickListener(int button) {
System.out.println("Button click=" + button);
}
@HandlerMethod(id = "canChange")
private static boolean canChange() {
System.out.println("Can change?");
return false;
}
public static void main(String[] args) throws Exception {
EventHandler handler = new EventHandler();
handler.provider.doStuff();
}
}
The class declares two methods. A private
instance method clickListener(…)
is a method that is called when a click event occurs. It is annotated with a HandlerMethod
annotation with a unique id
. This id
is used by:
this.provider.clickdListeners().add(this, "clickListener");
to point to a right method in a supplied object. The supplied identifier must be unique within a class scope, but can be reused in separate classes.
The private static
method canChange()
is called when a vetoableListener
is called. The code:
this.provider.setVetoableListener(new MethodPointer(this.getClass(),"canChange"));
set the pointer required by source object. It is notable that LinkSet requires a Class
object as an event target when a static
method’s id
is supplied.
Summary
LinkSet library is a little Perl that was formed through a constant irritation caused by Java’s default way of handling events. It is small, simple and easy to use. It is a drop in replacement that does not require any new syntax constructs like closures.
History
- 19th February, 2010: Initial post