Using functional Java with SFS2X Java API

With the release of patch 2.13.1 SmartFoxServer 2X adds support for lambda expressions in Java Extensions. Also we have added support for this new JDK 8 feature in the Java client API, starting from release 1.7.1

If you’re not familiar with lambda expressions we highly recommend to take a look at our introductory article where we illustrate the basics and show how to integrate the feature in your server side code.

In this new article we will focus on the client side of things but, before getting started, make sure you are already using the JDK 8 (or later) and the SFS2X Java API 1.7.1 (or higher)

ยป Client side functional style

This is an example of how a simple client looks like before functional Java was supported:

public class SFSClient implements IEventListener
{
	private SmartFox sfs;

	private void initSmartFox() 
	{
		ConfigData cfg = new ConfigData();
		cfg.setZone("BasicExamples");
		cfg.setDebug(true);
		
    	sfs = new SmartFox();
    	sfs.addEventListener(SFSEvent.CONNECTION, this);

    	sfs.connect(cfg);
	}

	@Override
	public void dispatch(BaseEvent event)
	{
	    if (event.getType().equals(SFSEvent.CONNECTION))
	    {
	    	boolean success = (Boolean) event.getArguments().get("success");
	    	
	    	if (success)
	    		System.out.println("We are connected");
			
			else
				System.out.println("Connection failed");

	    }
	}	    	
}

When we register client events (via SmartFox.addEventListner) we need to provide an object that implements the IEventListener interface. There are a couple of ways of doing this:

  • Creating a different class implementing IEventListener for every handler
  • Creating a single IEventListener implementation that deals with all the events via a series of “if” expressions

Both approaches have pros and cons. The former requires to create many classes and keep each handler separated which in turn can make things more complex when handlers need to talk to each other or access common state.
The second approach is a more convenient way that works for simple applications but at the cost of jamming one class with all the logic for each event.

With Java 8 lambda expressions we can improve the readability and implementation of handlers like this:

public class LambdaSFSClient
{
	private SmartFox sfs;

	private void initSmartFox() 
	{
		ConfigData cfg = new ConfigData();
		cfg.setZone("BasicExamples");
		cfg.setDebug(true);
		
    	sfs = new SmartFox();
    	sfs.addEventListener(SFSEvent.CONNECTION, this::onConnection);

    	sfs.connect(cfg);
	}

	@Override
	public void onConnection(BaseEvent event)
	{   	
		boolean success = (Boolean) event.getArguments().get("success");
	    	
    	if (success)
    		System.out.println("We are connected");
		
		else
			System.out.println("Connection failed");
	}	    	
}

Notice how we don’t need to implement the IEventListener interface in our main class and we’re now passing directly our custom method via the double colon (::) syntax.

The double colon operator is used to reference a method so that we can pass it around as any other object. Of course we’re not limited to passing methods from the current class, but we can also reference other methods such as in the example below:

public class LambdaSFSClient
{
	class CustomHandler
	{
		public void onConnection(BaseEvent event)
		{   	
			boolean success = (Boolean) event.getArguments().get("success");
		    	
	    	if (success)
	    		System.out.println("We are connected");
			
			else
				System.out.println("Connection failed");
		}
	}

	private SmartFox sfs;
	private CustomHandler ch = new CustomHandler();

	private void initSmartFox() 
	{
		ConfigData cfg = new ConfigData();
		cfg.setZone("BasicExamples");
		cfg.setDebug(true);
		
    	sfs = new SmartFox();
    	sfs.addEventListener(SFSEvent.CONNECTION, ch::onConnection);

    	sfs.connect(cfg);
	}	    	
}

Here we are using a custom class where we implement one or more handlers and we reference then directly, with the aforementioned double colon operator (similar to what we did in the previous example).

There is also the possibility to inline anonymous functions (a.k.a. lambda expressions) where appropriate:

sfs.addEventListener(SFSEvent.CONNECTION_LOST, (event) -> { System.out.println("We lost connection" ); });

This is usually okay for relatively small sections of code, otherwise referencing a method is the way to go.

Other resources

If you’re interested in learning more about functional Java and lambda expression we highly recommend a number of external articles: