Pages - Menu

Friday, September 21, 2012

Start Hacking With Java 8

This article aims to give some quick background on Java 8 and help you get set up so you can try out the new features. Java veterans (and PL veterans) won't learn anything new, this article is for general Java programmers who want to learn more about what's coming next year.

The list of planned features for Java 8 is available on the OpenJDK page.

I intend to follow up with articles describing each new feature in further details so stay tuned. In the mean time, you can read more about lambdas, virtual extension methods and type annotations.

Background

We have shifted from a single core world to a multi core world. In fact, we have reached a physical limit to how many transistors can fit on a chip. For this reason, to perpetuate Moore's law, today we achieve additional performance by parallelizing computations across several cores.

This shift brings new challenges. Programmers need to learn how to write software that parallelize gracefully to leverage multiple cores and therefore gain more performance.

Programming language designers can help by providing better abstractions and easier to use libraries with the goal to reduce the conceptual and syntactic gap between sequential and parallel expression of a computation.

Functional to the rescue?

Functional programming provides a mechanism to achieve this goal. In fact, a computation is described as a combination of functions that are side-effect free. In practice, this allows to specify what a computation does and let the compiler decide how to implement it. The programmer doesn't specify how the computation is implemented anymore.

Take as an example, a code to filter a list based on a condition in Scala:

val listOfRedBoxes = boxes.filter(b => b.getColor() == RED);

This is possible through the use of the filter method which abstracts away the internal filtering implementation. One can pass as an argument an anonymous function that evaluates to the filtering condition. Other programming languages such as Groovy, Clojure provide similar facilities to pass code as data, which many refer to as closures. As a result, the compiler can decide whether the filtering logic should be executed sequentially or in parallel.

Contrast the code above with the typical Java idiom that programmers write to filter a list based on a condition:

List<Box> listOfRedBoxes = new ArrayList<>();
for(Box b : boxes)
{
    if(b.getColor().equals(RED))
        listOfRedBoxes.add(b);
}

Here, the use of an accumulator and a for-each loop to describe the traversal logic of the filtering enforces a sequential execution. Sometimes a for-each loop is desirable as it is in-order, however, sometimes this specification is too tight and prevents additional performance.

Java 8 brings a revamped collection library together with a mechanism to pass code as data refered to as lambda expressions in order to facilitate writing code that parallelize gracefully.

The example above can be written as follows:

List<Box> listOfRedBoxes = boxes.stream().filter(b -> b.getColor().equals(RED))
                                .into(new ArrayList<Box>());

The traversal logic is not fixed by the language anymore and can be chosen by the library implementation or compiler. As a result, paralellism and out-of-order execution can be chosen to improve performance.

One could argue that passing code as data in Java is already possible via anonymous inner classes. However, lambda expressions bring many advantages over anonymous inner classes such as better readability, simpler semantics and stronger inference which we discuss in more details in the next article.

Set Up JDK8

At the time of this writing, there isn't a stable version of jdk8 yet. However, you can download an early access implementation of the lambda project and a separate early access implementation of the type annotations project.

After downloading the archive containing the jdk, set the environment variable JAVA_HOME to the path where the unpacked directory is located. To check it's set up correctly, try the following commands:

$ java -version
openjdk version "1.8.0-ea"
OpenJDK Runtime Environment (build 1.8.0-ea-lambda-nightly-h1171-20120911-b56-b00)
OpenJDK 64-Bit Server VM (build 24.0-b21, mixed mode)

$ javac -version
javac 1.8.0-ea

You can now try the following code:

import java.util.*;
public class TestLambda
{
    public static void main(String... args)
    {
        List<Integer> l = Arrays.asList(1,2,3,4,5);
        int sum = l.stream().map(x -> x*2).reduce(0, (a,b) -> a+b);  
        System.out.println(sum);
    }
}

Can you guess the output? Compile it and run it!

$ javac TestLambda.java 
$ java TestLambda
30

Note that most IDEs don't provide support for Java 8 yet so you will have to compile files using javac.

IntellJ seems to be the only IDE supporting lambdas at the moment.

Start hacking!

8 comments:

  1. Am I missing something or shouldn't the result be 55??

    ReplyDelete
  2. List listOfRedBoxes = boxes.filter(b -> b.getColor().equals(RED))
    .into(new ArrayList());

    Shouldn't this be new ArrayLIst()?

    ReplyDelete
  3. Square brackets got removed: I meant new ArrayList[opening bracket]Box[closing bracked]() instead of Integer.

    ReplyDelete
  4. Recent IDEA EAPs do understand lambdas.

    ReplyDelete