65.9K
CodeProject is changing. Read more.
Home

C# Like Event Handlers Pattern in Java

starIconstarIconstarIconstarIconstarIcon

5.00/5 (3 votes)

Mar 27, 2017

CPOL

1 min read

viewsIcon

12931

Simulating C# event handlers in Java

Introduction

For those of us who come from the C# world, sometimes it can be a bit difficult to deal with the way events are handled in Java with listeners. This pattern is implemented to solve that problem in a familiar way to .NET.

Background

A few weeks ago, for work reasons, I had to go to the Java world and leave behind .NET. Everything was fine until I had to face the listeners, although it is a fairly interesting (observer) pattern at times that can be a bit cumbersome. For this, I solved my problem in this way (implementing a pattern similar to the C # eventhandlers), although it is a solution that may not necessarily be definitive but could serve you on more than one occasion.

Using the Code

Thanks to @FunctionalInterfaces, it is possible to build a kind of .NET delegate.

What is a functional interface?

@FunctionalInterface
public interface IEvent<TEventArgs extends Object> {
    void invoke(Object source, TEventArgs eventArgs);
}

Now, we can define a reutilizable EventHandler class for event manage:

public class EventHandler<TEventArgs>
{
    private ArrayList<IEvent<TEventArgs>> eventDelegateArray = new ArrayList<>();
    public void subscribe(IEvent<TEventArgs> methodReference)
    {
        eventDelegateArray.add(methodReference);
    }
    public void unSubscribe(IEvent<TEventArgs> methodReference)
    {
        eventDelegateArray.remove(methodReference);
    }
    public void invoke(Object source, TEventArgs eventArgs)
    {
        if (eventDelegateArray.size()>0)
            eventDelegateArray.forEach(p -> p.invoke(source, eventArgs));
    }
    public void close()
    {
        if (eventDelegateArray.size()>0)
            eventDelegateArray.clear();
    }
}

This EventHanlder class allows us to subscribe, un-subscribe, invoke, clean up resources of our defined event.

For purposes of testing our code, we will create an event producing class and a consumer class (subscriber) of our event.

public class DummyEventProducer
{
    // The event
    public EventHandler<String> myEvent = new EventHandler<>();

    public void onMyEvent(String A)
    {
        myEvent.invoke(this, A);
    }
}
public class DummySubscriber {

    // The method will be subscribed to the event
    public void methodCallWhenEventGetTriggered(Object source, String eventArgs)
    {
        System.out.println("event fired with eventargs: " + eventArgs);
    }
}

Now the main entry point of our test app:

public class Main {

    public static void main(String[] args)
    {
        // A dummy producer
        DummyEventProducer producer = new DummyEventProducer();

        // A dummy subscribers
        DummySubscriber testingInstanceA = new DummySubscriber();
        DummySubscriber testingInstanceB = new DummySubscriber();
        DummySubscriber testingInstanceC = new DummySubscriber();

        // We create weak event references because we want to un-subscribe later
        IEvent<String> EventSink1 = 
        	testingInstanceA::methodCallWhenEventGetTriggered;
        IEvent<String> EventSink2 = 
        	testingInstanceB::methodCallWhenEventGetTriggered;
        IEvent<String> EventSink3 = 
        	testingInstanceC::methodCallWhenEventGetTriggered;

        // subscribe to the event on dummy producer
        producer.myEvent.subscribe(EventSink1);
        producer.myEvent.subscribe(EventSink2);
        producer.myEvent.subscribe(EventSink3);

        // fire the event on producer
        producer.onMyEvent("Hola MUNDO with decoupled subscriptions!");

        // unsubscribe to the event on dummy producer
        producer.myEvent.unSubscribe(EventSink1);
        producer.myEvent.unSubscribe(EventSink2);
        producer.myEvent.unSubscribe(EventSink3);

        // fire the event on producer again
        producer.onMyEvent("Hola MUNDO! with no events subscriptions :(");

        // IF YOU DON CARE ABOUT UNSUBSCRIBE 
        // YOU CAN CREATE STRONG EVENTS REFERENCES DIRECTLY TO THE SUBSCRIBER
        producer.myEvent.subscribe(testingInstanceA::methodCallWhenEventGetTriggered);
        producer.myEvent.subscribe(testingInstanceB::methodCallWhenEventGetTriggered);
        producer.myEvent.subscribe(testingInstanceC::methodCallWhenEventGetTriggered);

        // fire the event on producer again
        producer.onMyEvent("Hola MUNDO! with strong references 
                           subscriptions (cannot be un-subscribed one by one");

        // clear all subscriptions
        producer.myEvent.close();

        // fire the event on producer again
        producer.onMyEvent("Hola MUNDO! again");
    }
}

And that's it! I hope it will be useful for you.

Points of Interest

I do not recommend using strong references as they can generate memory leaks, but instead, try to use weak references, especially when it comes to events.