Implementing Runnables the Lambda Way

Java 8 introduced Lambda expressions. We’ve been told they would bring simpler easier-to-read programs. We’ve been told that Lambda expressions create multi-core friendly code. Functional programs created with Lambda expressions have fewer bugs, saving development time and maintenance costs. Rumor has it that the release of Java 8 was the moment when the rise of the oceans began to slow and our planet began to heal; it was the moment when we ended all language wars and secured Java’s image as the last, best hope on Earth.

I’m not saying it’s all true. That’s just what I’ve heard.

To decipher rumor from truth, we need to start with some basics. There is a lot of complex, albeit cool, stuff that the experts can do with Lambda expressions. We’ll start with a basic tutorial on how to use a Lambda expression to implement a Runnable.

Implementing Runnables with Lambda Expressions

In the beginning of Java threading, if you wanted to create a new thread, you extended Thread. Quickly, OOP snobs pointed out that you should be implementing Runnables and passing those into the new Thread() when constructing threads. Either approach resulted in a new class file in your package. It added clutter for the sake of a class that often was short, and only got used one place.

Then came anonymous classes. Ever since anonymous classes were introduced to Java, all the cool kids used them. They still had most of the code that a Runnable implementation in its own file had, but at least you didn’t need to jump to another file to figure out what the newly started thread was supposed to do.

Here’s your typical Runnable implementation circa pre-Java 8.

Thread t = new Thread(new Runnable() {
	public void run () {
		System.out.println("Look! I'm one of the cool kids!");
	}
});

t.start();

The above code instantiates a new Thread with an anonymous implementation of the Runnable interface. And, we do all of that just to call one function that isn’t even part of our Runnable implementation. That’s a lot of decoration, just to do one simple task in a new thread.

Now let us look at this same anonymous implementation using a Lambda expression.

Thread t = new Thread(() -> System.out.println("Now, I'm cooler than the cool kids!") );
t.start();

No new Runnable? No public void run () {}? That certainly is simpler than the traditional anonymous class. All you need is () -> and your method call to implement the whole anonymous Runnable. I feel like I was ripped off, wasting time for all these years doing all that extra code for no reason.

Oh, my wasted youth. If only I had had Lambda expressions! I could have taken up new hobbies and gone to the gym more with all the saved time Lambda expressions could have brought me! But the past is the past.

Multi-Line Lambdas

Now the more discerning reader might have realized, sometimes you want a loop of some kind surrounding your method call in your Runnable. No problemo. Java 8 allows you to place a typical expression block containing the code into your Lambda expression.

Thread t = new Thread( () ->
			{
				System.out.println("And I can count, too!");
				for (int i=1; i<=10; i++) {
					System.out.println( i );
				}
			} );
t.start();

As per the above example, just place curly braces around your code. It’s typical Java code ending in semicolons. Note: That is different than the single line Lambda expression earlier in this tutorial. There was no terminating semicolon after the System.out.println() in the single line example without the curly braces.

Defining a Runnable Using a Lambda Expression

Sometimes the code you want to put in your run() method of your Runnable is to long and complex to place inline, but not long enough and complex enough to justify placing out in its own file. In these cases, its nice to define the Runnable with a Lambda express, and then pass the not-so-anonymous Runnable into the new thread.

Runnable localRunnable = () ->
{
	System.out.println("The used-to-be-cool kids will be *so* jealous!");
};

Thread t = new Thread( localRunnable );
t.start();

Now you can pass around the Runnable you created with a Lambda expression. If you think that’s cool, you ain’t seen nothing yet!

Closures with Java Lambda Expressions

Now, let’s pretend you want a factory class to generate Runnables based off parameters passed in to a generate method. Off the cuff, you might think it’s time to break down and implement Runnable classes. Ick! Surely the thought of leaving your beloved Lambda expressions behind makes you sick to your stomach.

Never fear. Closures are here!

Closures are functions that take their environment with them after their creation. That means, if you create a Lambda expression that uses local variables in the method that created it, it remembers and uses those values even once it has been returned from the method.

Hold on to your hat. This is really cool.

package com.tgenedavis.closures;

public class MyRunnableFactory {

	public static Runnable generateCount( int max ) {
		return () ->
		{
			for (int i=1; i<=max; i++) {
				System.out.print( i + " " );
			}
			System.out.println();
		};
	}
}

All the MyRunnableFactory does is implement one method that returns a Runnable that counts to the max value passed in. Think about it. The variable max is locally scoped, and you are using it in a Lambda expression that gets passed around to be used (potentially) much later after the method is terminated. It’s okay if you get the shivers, here.

You can create Lambda expressions that use temporary values, but when that code gets passed out of scope, the methods implemented using the Lambda expressions remember the values they were created with. This means that you can now generate methods dynamically, quickly, without the hassle of dealing with creating whole classes with custom attributes to hold onto state.

Now for the code that uses the closure.

package com.tgenedavis.closures;

public class Counting {

	public Runnable countTo10;
	public Runnable countTo23;
	public Runnable countTo42;

	public static void main(String[] args) {
		Counting myCounter = new Counting ();

		myCounter.count( myCounter.countTo10 );
		myCounter.count( myCounter.countTo23 );
		myCounter.count( myCounter.countTo42 );
	}

	public Counting () {
		countTo10 = MyRunnableFactory.generateCount( 10 );
		countTo23 = MyRunnableFactory.generateCount( 23 );
		countTo42 = MyRunnableFactory.generateCount( 42 );
	}

	public void count( Runnable r ) {
		//take a breather before counting 
		//we are dealing with threads, after all
		try {Thread.sleep(1000);} catch ( Exception e ) {}

		new Thread( r ).start();
	}

}

We create three different Runnable instances, generated from the MyRunnableFactory.generateCount() method. Notice that each Runnable uses different internal values for terminating its counting. The local values for the max variable are retained and used when the Thread starts and the Runnable executes.

Summary

I have covered using Lambda expressions to quickly implement Runnables. This removes a lot of unnecessary decoration from the code, making the code easier to read and maintain. Lambda expressions can be one line of code or multiple lines of code. Many languages (unlike Java) only allow one line of code in a Lambda expression, so Java is extra cool.

Lambda expressions can make anonymous or defined Runnables. The Java compiler is smart enough to know that you want a Runnable when you use a Lambda expression during Thread construction. No special cast or marker is needed. The Java compiler is also smart enough to know you want a Runnable when you set a Runnable variable equal to a Lambda expression.

Lambda expressions keep local state. If you create a Runnable using a Lambda expression that refers to a local variable. The Runnable will retain the variable’s value even when the Runnable is passed out of the local scope.

In short, Lambda expressions definitely make cleaner, easier-to-maintain code. I’m not sure about the healing the planet, stopping the oceans from rising, or the end to wars bit.