Shaha Alam on 23 May 2018

A few weeks back, I decided to get involved in Java Challenge UK, by sitting on the competition’s jury panel. A bit of context: the Java Challenge is a gamified competition, with one league for professionals and one for students, that consists of multiple-choice questions. As part of the “jury duty”, I had the task of writing one of these questions. I decided to write my question around lambdas; read on to find out a little bit about why...

OK, before I begin, full disclosure, I can't answer the question I myself submitted to the exam.  But before we get into that, let's talk lambdas!

Lambdas are an old concept in programming. In fact, like many concepts in computing, it existed as an idea long before the first computer. In short, they let you define functionality, anonymously. So, what that means is that you can define behaviour, without giving that behaviour a name. Or more precisely, you can define functionality without being obliged to give it a name.  The more that can be inferred by the compiler, the less code you're having to write, and the fewer bugs or side effects there will be in your code. Take all of these things, and you'll see that it will be much easier to maintain overall! Also, why would you want to write something that isn't necessary?

Consider an event-driven application; you're often writing code where the flow of control is outside of your... control.  Often you're writing methods to handle specific scenarios, not really knowing when that scenario will occur. In this situation, your methods are known to others, Controller classes, but you don't actually call them yourself.

Eg, imagine you've implemented the following utility class:

public class EventLogger {
public void handle(Event e) {
Logger.log(e);  //some external class that does logging
}
}

 

Your common utility class will be used in a number of places where events are fired.  An example of telling a controller class that your utility exists, might be:

//tell controller class about yourself
controller.registerEventLogger(new EventLogger());

 

Your class is among several event handler classes, all of which are notified in order to do different things when an event occurs. So the code might look like this:

interface EventHandler {
public void handle(Event e);
}

public class EventLogger implements EventHandler{
public void handle(Event e) {
Logger.log(e);
}
}

public class MyClass {

public static void main(String[] args) {
new Controller().register(new EventLogger());
}
}

 

As you can see, we're already starting to write boiler plate code and repeating ourselves.  For example, by implementing the EventHandler interface, we're having to define the function being implemented again.  This makes no sense if the interface only has one method, like we saw in the example above. 

Each EventHandler implementation has its own class file, and their only real purpose is to 'own' an implementation of the handle(Event e) method. We can reduce some of the 'noise' by using anonymous inner classes to define each EventHandler implementation, as I show below:

//same interface definition as before

class myClass {

public static void main(String[] args) {
Controller controller = new Controller();

//register an event logger implementation
controller.register(new EventHandler() {
public void handle(Event e) {
Logger.log(e);
}
});

//register an event scheduler implementation
controller.register(new EventHandler() {
public void handle(Event e) {
Schedular.add(e);
}
});
}
}

So, we've avoided creating a number of extra classes, by defining anonymous classes. But the elephant in the room is Java's lack of treating methods as first-class citizens in the language.  Really, what we want is to define a set of common utility methods, but Java forces us to classify them and put them in an object. So rather than focus on the implementation logic, we're wasting time accommodating the limitations of the language.

And we're still writing a fair amount of redundant code. For example, each implementation of the EventHandler interface has to have the handle(Event e) method.  But is it really strictly necessary to tell the compiler which method you're implementing? EventHandler only has one method, so why cant the compiler infer that information?

This is where lambdas come in, to give us a little syntactic sugar and reduce the amount of unnecessary coding. Before we begin, it's important to note that lambda expressions only work with functional interfaces. By that, I mean an interface with only one method.

Let's see the code above, using lambda expressions:

class myClass {

public static void main(String[] args) {
Controller controller = new Controller();

//register an event logger implementation
controller.register((e) -> Logger.log(e));

//register an event scheduler implementation
controller.register((e) -> Schedular.add(e));
}
}

When the compiler looks at a lambda expression, it has to infer information the developer was previously forced to specify. The code is much more concise, and the developer only has to define what the actual behaviour is, given the input parameters. This is the closest Java gets to treating methods as first-class citizens, and a glimpse of how useful lambdas can be..

In a follow-up post, we'll look at lambda block expressions (because methods usually execute more than one statement!), and at using generics with lambdas.

...now, back to the UK Java Challenge.

If you haven't already, head over to the challenge page and give it a go! Don't worry if you find it hard, many of the questions really do test your understanding of the language. I volunteered to submit a question about lambdas because it's an improvement to the language which is long overdue, and could go a long way in reducing all the boiler plate code we've become accustomed to writing, without asking whether there could be an easier way.

Vikram Setia,

Chief Commercial Officer