by

What’s New in Java 8? Part I.

If you think I’m a little late to the party, you’re absolutely right. Java 8 was released in March 2014, well over a year ago, and there has been plenty of time to look at version 8’s new features. Unfortunately, I’m still crawling my way through millions of lines of Java 7 legacy code at work, and it is not until now that the client has decided to make the jump to version 8. The reason is that, as of April 2015, Oracle stopped releasing public updates for version 7, and gaping security holes will no longer be patched. Thank you, Oracle! You might think this is a sarcastic “thank you”, but it’s really not. If it wasn’t for the fact that there will be no more security updates for Java 7, the client would probably never have decided to upgrade.

Java 8 comes with a range of new features and enhancements, conveniently summarized by Oracle in an article aptly titled What’s New in JDK 8. Among the changes are some that were supposed to be in Java 7, but that, for various reasons, were postponed to Java 8. Some of the new features are changes developers have been screaming at Oracle (and Sun) to implement for years. Now that they have finally managed to get them in there, we should try to use them, shouldn’t we? This was originally intended to be a single post covering the most interesting new titbits of Java 8, but I quickly realized that it was too much to get through in one post, so I split it in to two parts.

Now, let’s have a look at a small set of the new features and enhancements in Java 8. I’ll focus on the features hat I’ll benefit most from during a typical work day. All the code examples used are available on GitHub.

Lambda Expressions

All right, confession time! The first time I saw lambda expressions, they looked like the programming equivalent of black magic. They still, to some extent, do, but I’m hoping that’s because of my low lambda expression mileage. So, what is a lambda expression anyway? Here’s Oracle’s description:

[Lambda expressions] enable you to treat functionality as a method argument, or code as data. Lambda expressions let you express instances of single-method interfaces (referred to as functional interfaces) more compactly.

I’m not sure how much clearer that made things, so let’s have a look at an example. To prevent the code samples from getting wider than the page, I’ve taken the liberty to omit the “final” keyword where it would be natural to use it. Also, most of the code below won’t compile as-is, since I’ve also omitted the class declarations.

Here’s a snippet of code that takes a List of Integers and prints all the Integers in the list that are within a given range:

1
2
3
4
5
6
7
public static void printIntegers(List<Integer> integers, int min, int max) {
    for (Integer anInteger : integers) {
        if (anInteger > min && anInteger < max) {
            System.out.println(anInteger);
        }
    }
}

That is all nice, but the method is very specific. What if we wanted the method to be a bit more generic and give us the option to specify the test criteria as an input to the method? Doing it this way, we would not have to write a separate method for every different combination of test criteria. A common pre-Java 8 approach would be to define an interface and use an implementation of that interface as input to the method. First, the interface:

1
2
3
public interface CheckInteger {
    boolean test(Integer integer);
}

And then the refined print method, now using the interface as a parameter:

1
2
3
4
5
6
7
public static void printIntegers(List<Integer> integers, CheckInteger tester) {
    for (Integer integer : integers) {
        if (tester.test(integer)) {
            System.out.println(integer);
        }
    }
}

Nifty! Now we have a pretty generic method for printing integers that match our test criteria. When we call the method, we have to provide it with an implementation of the CheckInteger interface to tell the method how we want it to print the integers. Below this is done using an anonymous class:

1
2
3
4
5
6
printIntegers(Arrays.asList(1, 2, 3, 4), new CheckInteger() {
    @Override
    public boolean test(Integer integer) {
        return integer > 1 && integer < 4;
    }
});

Hurrah! That does the job, but it isn’t very readable and it also contains what seems to be a lot of unnecessary code: We have to create an entire class based on an interface, tell the compiler that we want to override a method in that class before we finally come to the part of the code that is actually interesting: The test criteria at line #4. What if we could skip all the boiler plate code and only send the test criteria, a piece of code, to the method as an input parameter?

This particular challenge is a nice use case for lambda expressions:

1
printIntegers(Arrays.asList(1, 2, 3, 4), i -> i > 1 && i < 4);

Oh, man – look at that! Everything you saw in the previous code sample condensed down to a single line. It’s possible to use lambda expressions like this because the CheckInteger interface is a functional interface: A functional interface is any interface that contains only one abstract method, and because of that you can omit the name of that method when you implement it. Java 8 comes with a standard functional interface, Predicate, which contains a single method, “test”, and is meant to be used for the kind of scenario we’re looking at now. Let’s modify the declaration of the printIntegers method slightly so it uses the standard Predicate interface instead of our own CheckInterger interface:

1
2
3
4
5
6
7
public static void printIntegers(List<Integer> integers, Predicate<Integer> tester) {
    for (Integer integer : integers) {
        if (tester.test(integer)) {
            System.out.println(integer);
        }
    }
}

Now it’s possible to remove the unused CheckInteger interface from the code. Come on, delete it! The less code, the better. Note that the lambda expression looks a lot like a method declaration. You can consider lambda expressions as anonymous methods — methods without a name, in the same way that anonymous classes are classes without a name.

Method References

Method references let you refer to and call an existing method using lambda expressions. Let’s have a look at a simplified Java 7 version of the printIntegers method. This version only prints integers without doing any filtering.

1
2
3
4
5
public static void printIntegers(final List<Integer> integers) {
    for (final Integer integer : integers) {
        System.out.println(integer);
    }
}

The for-loop can be condensed down to a single line using a new method in the Iterable interface together with method reference:

1
2
3
public static void printIntegers(final List<Integer> integers) {
    integers.forEach(System.out::println);
}

The Iterable.forEach method “performs the given action for each element of the Iterable […]”. The action, in this case, is a call to System.out.println, but we’re not calling it like we would normally do. Instead, we use method reference and the fact that forEach behaves the way it does; performing the given action on for each element in the list of integers.

Lambda expression debugging

Over the years, I’ve learned to love the Debugger. I often find myself knee deep in legacy code, written by developers who obviously don’t adhere to the Golden Rule of Programming, coined by Damian Conway: “Always code as if the guy who ends up maintaining your code will be a violent psychopath who knows where you live.” In these cases, the only way to understand what’s going on in the code is to fire up the debugger. But how do we debug lambda expressions?

Let’s use an extended version of one of the earlier codes snippets as an example.

1
2
3
4
5
6
7
8
9
10
11
12
13
public class PrintIntegers {
    public static void printIntegers(List<Integer> integers, Predicate<Integer> tester) {
        for (Integer integer : integers) {
            if (tester.test(integer)) {
                System.out.println(integer);
            }
        }
    }
 
    public static void main(final String[] args) {
        printIntegers(Arrays.asList(1, 2, 3, 4), i -> i > 1 && i < 4);
    }
}

In a world without lambda expressions, I would probably place a breakpoint at line #4 and defined a new watch on “tester.test(integer)”. But what if we want to have a break point on the actual testing code and not only see the return value of “tester.test(integer)”? In that case, we have to place a breakpoint on line 11. When “tester.test(integer)” runs, the debugger will jump down to line 11 and you can go ahead and inspect how the code in the lambda expression – or even parts of it – works for any given value of the variable used, “i”.

At least this is how it’s done in version 14 of the excellent IntelliJ IDEA. If you’re using NetBeans, Eclipse or some other Java IDE, the way to debug lambda expression might be different.

Further reading

This short introduction merely touches the tip of what is the Lambda Expression Iceberg. Oracle’s own lambda expressions primer will give you further insights into the topic, and there is a lot of other articles and tutorials on the internet to push you along.

Default and static methods in interfaces

Interfaces are great: They define a contract. That a class implements an interface guarantees that methods defined in the interface can be called on an instance of the implementing class. But what happens if the interface maintainer decides to add new methods to the interface? If the interface is not versioned somehow, your implementing class will break and compiler will throw a fit, complaining that an implementation of the new interface method is missing from your class.

A new Java 8 feature, default methods, can be used to solve this. Default methods enable you to add new functionality to the interfaces of your libraries and ensure binary compatibility with code written for older versions of those interfaces.

By using the keyword “default”, you can define an actual implementation of a method in your interface. Here’s a quick example:

1
2
3
4
5
6
7
public interface InterfaceWithDefaultMethod {
    void anInterfaceMethod();
 
    default void aDefaultMethod() {
        System.out.println("I'm the default method!");
    }
}

The interface contains two methods. First, it’s anInterfaceMethod, which is the kind of method declaration you are used to seeing in Java interfaces. Then, it’s aDefaultMethod, which is a complete and valid interface method in Java 8. An implementation of the interface would look something like this:

1
2
3
4
5
public class ClassImplementingInterface implements InterfaceWithDefaultMethod {
    public void anInterfaceMethod() {
        System.out.println("I'm the interface method!");
    }
}

Notice the missing implementation of aDefaultMethod. This is perfectly legal in Java 8 and makes sure you can add new methods to your Java 8 interfaces without running the risk of breaking every single implementation. Creating an instance of ClassImplementingInterface and then running the aDefaultMethod will result in the text “I’m the default method!” being printed. If you need to implement your own version of the default method, you can override aDefaultMethod in the implementing class in exactly the same way as you would override non-default interface methods.

In addition to default methods, you can now also define static methods in interfaces. This can be helpful when you want helper methods that are specific to an interface to stay in the interface, and not in a separate class.

And on that, I think it’s time to end the first part of the “What’s New in Java 8” series. Be sure you have a look at the example code on GitHub and check back every hour for the second and last part of “What’s New in Java 8”.

Write a Comment

Comment