Functional Java in server side Extensions

With the release of patch 2.13.1 SmartFoxServer 2X adds support for functional Java and lambdas in server side Extensions. In this article we’re going to take a closer look at how we can use lambdas in our server side code.

If you’re familiar with languages such as Python, Ruby or Javascript you have probably¬†used functions as any other object and pass them around as parameters to other functions and methods. Also you can inline anonymous functions that can be passed to any method requiring it.

In Java this has never been possible until the release of JDK 8. Before that, developers could emulate a similar mechanism with anonymous classes, such as in the code below:

Thread thread = new Thread
(
	new Runnable()
	{
		@Override
		public void run()
		{
			System.out.println("Thread is running...");
		}
	}
);

thread.start();

This is a good example of the infamous “verbosity” in Java that is often complained about by developers. And it’s probably a fair criticism given that every time we want to pass a method around we have to create an anonymous class wrapping the actual method, and implement the “method’s interface” (i.e. the class the acts as template for the implementation we’re passing around).

Now that Java supports lambdas (or anonymous functions) we can express the same functionality in a more readable way:

Thread thread = new Thread( () -> { System.out.println("Thread is running..."); } ); 

What we’re doing here is defining an anonymous function that takes no parameters (hence the “()”) and inlining it after the arrow operator. In other words we can now define an anonymous function like this:

(paramA, paramB) -> { // body of the function };

and we can treat this as any other object in Java, pass it to other methods etc. In parenthesis we specify the arguments of the function, followed by the arrow operator (->), followed by the body of the function in curly brackets, as in any other method.

Functional interfaces

Before we proceed with using lambdas in SFS2X we need to mention functional interfaces which define the function in terms of parameter types and return value types. Essentially any interface with a single abstract method is also a functional interface.

The new JDK 8 provides a lot of these to cover the basic needs of functional programming, such as empty functions, predicates and more. To illustrate how this works, we’ll implement a simple test that calculates the occurrence of certain characters in a String. For instance we may want to calculate all of the characters in a message, or just the alphanumeric ones, or maybe only the upper cases, etc.

To do this we can create different functions that calculate different occurrences and pass them around as parameters. The following is our main method:

public int countCharacters(Function<String, Integer> fn, String str)
{
	int count = fn.apply(str);
	return count;
}

Here we require two parameters: a function that takes a string and return an integer and the string to search. The Function type is a functional interface provided by the JDK (under the java.util.function package) and declared as Function<T, R> where T is the type of the input and R is the type of the return value.

String testStr = "This is a test string";
Function<String, Integer> myfn = (str) -> { return str.length(); };

int res = countCharacters(myfn, testStr);
System.out.println("Result => "+ res);

public int countCharacters(Function<String, Integer> fn, String inputStr)
{
	int count = fn.apply(inputStr);
	return count;
}

Here we define a function that calculates the number of characters and then pass it to our main countCharacter method. This enables us to implement as many different functions as we need and dynamically pass them to count characters in multiple ways.

We can also inline the function directly, if needed:

int res = countCharacters((str) -> { return str.length(); }, "This is a test string");
System.out.println("Result => " + res);

 

Integrating in SFS2X

Assuming you’re familiar with how Extensions work in Java, (if not please see here)
this code should look familiar:

public class MyExtension extends SFSExtension
{
    @Override
    public void init()
    {
         // Add a new Request Handler
        addRequestHandler("echo", EchoHandler.class);
    }
 
    public class EchoHandler extends BaseClientRequestHandler
    {
        @Override
        public void handleClientRequest(User sender, ISFSObject params)
        {
            send("echo", resObj, sender);
        }
    }
}

We create a request handler with the command “echo” and register it with the Extension. The handler in turn receives the request and sends the same data back to the client.

With the new release the BaseClientRequestHandler class is now implementing a functional interface that looks like this:

@FunctionalInterface
public interface IClientRequestHandler
{
	void handleClientRequest(User sender, ISFSObject params);
}

Notice the @FunctionalInterface annotation. This isn’t mandatory and it’s more of an informative tool for developers as the compiler will detect functional interfaces regardless of the annotation.

This is how we can translate the previous code in functional style:

public class MyExtension extends SFSExtension
{
    @Override
    public void init()
    {
         // Add a new Request Handler
        addRequestHandler("echo", (sender, params) -> { send("echo", resObj, sender) });
    }
}

We have eliminated the class definition entirely and since the method is very simple we can inline it directly. Alternatively we can use the double colon syntax (::) as shown below, which allows to reference methods and pass them as objects.

public class MyExtension extends SFSExtension
{
    @Override
    public void init()
    {
         // Add a new Request Handler
        addRequestHandler("echo", this::handleEchoRequest);
    }

    private void handleEchoRequest(User sender, ISFSObject params)
    {
    	send("echo", resObj, sender);
    }
}

Which style to use

Passing functions inline or via the double colon operator is very handy and the best option for short sections of code. However the “traditional” way of implementing a dedicated class per handler remains the recommended way to organize the server code.

This is especially true when a request or event handler deals with a complex set of actions that span across multiple methods and depends on many other classes. In those cases being able to keep your handlers separated and organized is probably best and will help maintaining the code in the long run.

The bottom line is that lambdas are an extra tool in our bag to use when it is appropriate and to get rid of unnecessary Java boilerplate code. You can not only integrate the functional aspects provided by the SFS2X server API, but also take advantage of it in the game logic.

Other resources

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