Posts tagged: java8

JVx and Java 8, Better Lambda support in 2.4

Post to Twitter

Already at the beginning of this year we started to improve the support for Lambdas in JVx. Now with 2.4 only a few days away, I'm happy to announce that we managed to improve it dramatically! Our events are now supporting basically every method which you can imagine as handler.

But let's not get ahead of ourselves, shall we? As most of you know, our own event handler scheme had support for basically five different variations of listeners:

private void initializeUI()
{
    button.eventAction().addListener(new UIActionListener() { ... });
    button.eventAction().addListener(this, "doActionA");
    button.eventAction().addListener(this, "doActionB");
    button.eventAction().addListener(this, "doActionC");
    button.eventAction().addListener(this, "doActionD");
}

public void doActionA()
{
    // A simple method with no parameters.
}

public void doActionB() throws Exception
{
    // A simple method which can throw *any* exception.
}

public void doActionC(UIActionEvent pActionEvent)
{
    // A method with the correct signature.
}

public void doActionD(UIActionEvent pActionEvent) throws Exception
{
    // A method with the correct signature which can throw *any* exception.
}

This scheme allows us to wire up basically any method to the event, and even to wire the same method to different events. Behind the scenes there is some reflection magic going on which I won't describe here, but with Lambdas entering the stage this changes quite a bit. The good thing about Lambdas is that they are fitting neatly into the already existing interface structure, so you can replace any interface implementation which has only one method with a lambda.

That means that you can do something like this:

private void initializeUI()
{
    button.eventAction().addListener(this::doActionA);
}

private void doActionA(UIActionEvent pActionEvent) throws Exception
{
    // The correct implementation.
}

But if you wanted to use a method without parameters you were out of luck until now, because listener interfaces always expect a parameter. With 2.4 there will be a new interface, called IRunnable, which provides a method which does not accept any parameter and can throw any exception and the EventHandler will also accept listeners which implement this interface. That means that the scheme outlined above is now fully possible with lambdas.

private void initializeUI()
{
    button.eventAction().addListener(new UIActionListener() { ... });
    button.eventAction().addListener(this::doActionA);
    button.eventAction().addListener(this::doActionB);
    button.eventAction().addListener(this::doActionC);
    button.eventAction().addListener(this::doActionD);
}

private void doActionA()
{
    // A simple method with no parameters.
}

private void doActionB() throws Exception
{
    // A simple method which can throw *any* exception.
}

private void doActionC(UIActionEvent pActionEvent)
{
    // A method with the correct signature.
}

private void doActionD(UIActionEvent pActionEvent) throws Exception
{
    // A method with the correct signature which can throw *any* exception.
}

And more good news, did you notice that the visibility of these methods changed from public to private? With the new Lambda scheme these methods no longer need to be public, they can have any visibility and will still work as intended.

So JVx 2.4 is the release when it comes to Lambda support, and everyone who has the possibility to already use Java 8 can now enjoy full support for them.

JVx and Java 8, Events and Lambdas

Post to Twitter

With version 8, Java has received support for lambda expressions and of course also JVx and its users directly profit from this new feature.

In case you do not know what a lambda expression is, it is basically the possibility to inline functions, sometimes also called "anonymous functions" and should not be confused with "anonymous classes", which Java has supported for quite some time now. Let's look at a simple example of anonymous classes:

new Thread(new Runnable()
{
    public void run()
    {
        // your code
    }
}).start();

This launches a thread that does something. The Runnable in our example is the anonymous class. Now with the new lambda support we don't need to write the interface implementation, but can directly tell it to run a function:

new Thread(() -> { /* your code */ }).start();

The empty parentheses and the arrow indicate a new anonymous function. The parentheses contain the list of parameters of the function that is going to be invoked, in our case it is empty because Runnable.run() does not have any parameters. So the anatomy of a lambda looks like this:

new Thread((optional explicit cast to target interface)(parameters) ->
{
    function body
});

Internally this is still compiled into an anonymous class, but the code becomes a lot smoother and easier to read.

Additionally it is now possible to reference functions directly, like this:

// instance
new Thread(this::worker).start();
// static
new Thread(YourWorkClass::worker).start();

Especially the last possibility is very exciting, as our event system has a very similar mechanism but until recently did not have compile-time safety and checks.

The new lambda feature is backwards compatible and can easily be used in JVx. Here is a simple test window that shows the new lambda expressions in combination with JVx events:

import javax.rad.genui.component.UIButton;
import javax.rad.genui.container.UIFrame;
import javax.rad.genui.layout.UIBorderLayout;
import javax.rad.ui.event.UIActionEvent;

public class LambdaShowcaseFrame extends UIFrame
{
    public LambdaShowcaseFrame()  
    {
        initializeUI();
    }

    public void doJVxAction(UIActionEvent pActionEvent)
    {
        System.out.println("JVx");
    }

    public void doLambda2(UIActionEvent pActionEvent)
    {
        System.out.println("Lambda 2");
    }

    private void initializeUI()
    {
        //before Java 8
        UIButton buttonJVx = new UIButton("JVx Style");
        buttonJVx.eventAction().addListener(this, "doJVxAction");

        // with Java 8

        UIButton buttonLambda1 = new UIButton("Lambda 1");
        buttonLambda1.eventAction().
                addListener(pActionEvent -> System.out.println("Lambda 1"));

        UIButton buttonLambda2 = new UIButton("Lambda 2");
        buttonLambda2.eventAction().addListener(this::doLambda2);

        setLayout(new UIBorderLayout());
        add(buttonJVx, UIBorderLayout.NORTH);
        add(buttonLambda1, UIBorderLayout.WEST);
        add(buttonLambda2, UIBorderLayout.EAST);
    }
}