What’s New in Java 8? Part II

This is the second and final part of a two post series on some of the new features and enhancements in Java 8.

This is the second and final part of a two post series on some of the new features and enhancements in Java 8. In the first post, we covered lambda expressions, method references, default methods and static methods in interfaces, and looked briefly at how we could debug lambda expressions. Since I did all the necessary Java 8 introductions in the first part, let’s just get on with what you came here for: A second dip into what’s new in Java 8.

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 omitted the class declarations - but all the code examples used are available in compilable and runnable versions on GitHub, so why don’t you go ahead; clone that repo and knock yourself out.

java.util.Optional

One of the biggest pitfalls in Java is the dreaded NullPointerException, or NPE for short. An NPE is an unchecked exception that will only be thrown at runtime and when the NPE bomb goes off, bad things are likely to happen. Preventing an NPE in your code really isn’t that hard, it’s just tedious and creates code that should be unnecessary.

Let’s say you use a badly documented third party API. The API contains a method that you know returns a List of Integers because it says so in the method signature and you use the method in your code like this:

List<Integer> integers = ThirdPartyAPI.getIntegers();
System.out.println(integers.size());

The problem with this code is that you don’t know what the third party implementing the API has decided to return if there are no Integer objects to return. Will the method return an empty list? Or will it return “null”? If the former is the case, you’re home free, but if the latter is the case, line #2 will cause an NPE to be thrown. It might be that you know that the method returns an empty List if there are no Integer objects to return - you could have written a unit test that somehow proves it or even reverse engineered the third party code - so you feel that the above code is safe. But what happens if the API developer for some reason decides to change the implementation so that “null” is returned instead of an empty list? Then you’ve suddenly got a major “whoopsie” in your code. It’s not your fault, per se, but I doubt that your client cares much about whose fault it is.

In our case, we’re lucky enough to have access to the source code for the third party API:

public static List<Integer> getIntegers() {
    List<Integer> integers = null;
    if(System.currentTimeMillis() % 2 == 0) {
        integers = Arrays.asList(1, 2, 3, 4);
    }
    return integers;
}

This method returns null and not an empty list, which means we have to harden our own code to cope with the null value. A viable post-Java 8-solution would be to enclose all NPE-prone code in a null-check:

final List<Integer> integers = ThirdPartyAPI.getIntegers();
if (integers != null) {
    System.out.println(integers.size());
}

Another alternative would be to enclose the call on line #3 in a try/catch-block, but that would mean even more code. Wouldn’t it be better if you knew could be almost certain the return value would be an actual object? The new Java 8 Optional can help with that1. Optional is a container object which might or might not contain a non-null value. Let’s say that the API returned an Optional instead:

public static Optional<List<Integer>> getIntegers() {
    List<Integer> integers = null;
    if(System.currentTimeMillis() % 2 == 0) {
        integers = Arrays.asList(1, 2, 3, 4);
    }
    return Optional.ofNullable(integers);
}

The API developer has made two changes to the original method. The first change is to modify the method signature, the other is to enclose the return value in an Optional container. The “Optional.ofNullable(integers)” call creates an empty Optional if integers object is null and an Optional containing the integers object if it’s non-null. Our code, which is using the API, would now look something like this:

Optional<List<Integer>> anOptional = ThirdPartyAPIUsingOptional.getIntegers();
System.out.println(anOptional.orElse(Collections.emptyList()).size());

Here we call the size() method of the list contained in the Optional, or on an empty list of the Optional is empty.

Note that there is still a major gotcha here. The new Optional object does not eliminate NPE problems caused by a developer being a complete moron. It’s still up to the API developer to use Optional correctly. If he still insists on returning null instead of an empty Optional when there are no Integer objects to return, we’re back to square one.

java.util.stream

The new java.util.stream package contains classes to support functional-style operations on streams of elements, such as map-reduce transformations on collections. Using the functionality in the java.util.stream package, you can create sequences of aggregate operations on collections, arrays, generator functions, or I/O channels. Such a sequence is called a pipeline. Let’s say you have a collection of doubles and you want to find the arithmetic mean of all doubles in the collection that are greater than zero.

In a world without java.util.stream and lambda expressions, one possible way to solve this challenge could look like this:

public static void getMeanOfAllDoublesGreaterThanZero(ist<Double> doubles) {
    int numberOfDoubles = 0;
    double doublesSum = 0;
    for (Double aDouble : doubles) {
        if (aDouble > 0) {
            numberOfDoubles++;
            doublesSum += aDouble;
        }
    }
    System.out.println(doublesSum / numberOfDoubles);
}

That sure is a lot of code to do a pretty simple calculation. In Java 8, however, we can, with the help of streams, lambda expressions and method references, do everything above in a single (albeit somewhat wide) line of code:

public static void getMeanOfAllDoublesGreaterThanZero(List<Double> doubles) {
    doubles.stream().filter(d -> d > 0).mapToDouble(Double::new).average().ifPresent(System.out::println);
}

Each of the method calls up to, and including, “mapToDouble” is an intermediate operation and will return a new stream for the pipeline. Stream pipelines are ended with a terminal operation, in our case the call to “average”. This call returns an Optional that, as we saw in the previous section, might or might not contain a non-null value. If a non-null value is present, it’s consumed by System.out.println, which is called by using a method reference. You don’t have to use a method reference, a classic call to System.out.println() would work just as well. But I just learned about method references in the previous post in this series and now I want to see what I can use them for.

Here’s a version of the above that doesn’t use method reference to print the result of the stream pipeline:

public static void getMeanOfAllDoublesGreaterThanZero(List<Double> doubles) {
    System.out.println(doubles.stream().filter(d -> d > 0).mapToDouble(Double::new).average().getAsDouble());
}

Which version is most readable is up for debate.

Date-Time APIs

No matter what kind of programming work you do, handling dates and time in your code is something you’ll have to manage at some point. At first glance, it doesn’t look very challenging, but throw leap years, different calendars and time zones into the mix and you’ll soon wish you had stayed in bed instead. Thankfully, Java comes with a range of APIs to help you cope with date and time operations.

But the Java date and time APIs has always been kind of crappy. They suffered from numerous issues caused by bad design decisions in early Java versions and because of Sun’s (and later Oracle’s) obsessive need to keep new Java versions backwards compatible, some of the problems were never really fixed. This led to the birth of popular third party APIs like Joda-Time, which has pretty much replaced the core Java APIs and become the de facto way of handling dates and time in Java.

This isn’t necessarily a bad thing, but in my opinion a basic thing like date and time operations should work out of the box using nothing but core APIs. Now, Oracle has finally made a real effort to solve everything date and time related with a completely new package of code, java.time.

Here is a little happy-fun-time with the APIs pre-Java 8:

public static void main(final String[] args) {
    try {
        // Find the current year.
        int currentYear = Calendar.getInstance().get(Calendar.YEAR);

        // Make a date and check if it's before today or not.
        Date aDate = new SimpleDateFormat("yyyy-MM-DD HH:mm:ss")
                .parse(currentYear + "-01-01 00:00:00");
        isBeforeOrAfterToday(aDate);

        // Add a year to the date and check if it's before today or not.
        final Calendar anotherDate = Calendar.getInstance();
        anotherDate.setTime(aDate);
        anotherDate.add(Calendar.YEAR, 1);
        isBeforeOrAfterToday(anotherDate.getTime());
    } catch (final ParseException e) {
        e.printStackTrace();
    }
}

private static void isBeforeOrAfterToday(final Date aDate) {
    if (aDate.before(new Date())) {
        System.out.println("The date is before today.");
    } else {
        System.out.println("The date is after today.");
    }
}

Wow. Just wow. That sure was a lot of hassle. There’s even a checked exception in there we have to catch. Now, let’s do exactly the same thing with the new java.time APIs in Java 8:

public static void main(final String[] args) {
    LocalDateTime aDate = LocalDateTime.of(LocalDate.now().getYear(), 1, 1, 0, 0, 0);
    isBeforeOrAfterToday(aDate);

    LocalDateTime anotherDate = aDate.plusYears(1);
    isBeforeOrAfterToday(anotherDate);
}
private static void isBeforeOrAfterToday(LocalDateTime aDate) {
    if (aDate.isBefore(LocalDateTime.now())) {
        System.out.println("The date is before today.");
    } else {
        System.out.println("The date is after today.");
    }
}

That looks a lot better. We’re not only rid of the horrendous checked exception, the code is also a lot more readable and easier to understand than the pre-Java 8 version. I’ve even taken the liberty to move the code that finds the current year into the call to the method that creates “aDate” since it’s an unambiguous one-liner. I’ve also removed all the comments because the code pretty much explains itself now.

Lovely!

There is a lot of new stuff in the java.time package, way too much to cover here. The most important thing to take away from this section is that Java 8 should be capable of handling all your date and time needs through the core APIs. And if there’s something you need to do that’s not in there, the APIs are easily extendable. Oracle has a tutorial you can read through to get an even better idea on how you can use the new APIs.

Final thoughts

Java 8 is filled with goodies that you should start using right away. There really is no good excuse not to, unless you are unable to make the jump to Java 8 for some reason. That reason is perhaps a client that doesn’t see the benefit of upgrading the code base to Java 8. The problem is usually that clients tend to see short term economical benefits, and in this case, there probably are none.

But the long terms benefits are potentially huge. One solid argument for updating to Java 8 is the lack of security patches for previous versions. You don’t want to get hacked, do you? Getting hacked is expensive! Another long term benefit could be legacy code maintenance. Some of the new features and enhancements in Java 8, like lambda expressions and method references, means that you can write less code than before and achieve the same end result. Fewer lines of code usually means decreased legacy maintenance cost. Fewer lines of code also means that your time-to-market will decrease, giving you a better chance to get that new killer feature ready before your competitors. And what does that mean? More money!

Yes, I’m focusing on the financial benefits here, because that’s, at least in my experience, the only arguments clients seem to really understand. That Java 8 can potentially make your work day as a Java programmer even more fun that it already is, doesn’t normally matter much to clients and employers - but I’m sure it matters a whole lot to you.

So go ahead. Have some fun.


  1. You can use this feature even if you’re still using a version of Java prior to version 8. It’s not that hard to implement the functionality of Optional on your own, or use a library that already contains an Optional, like Google’s Guava↩︎


Feedback

Do you have any thoughts you want to share? A question, maybe? Or is something in this post just plainly wrong? Then please send an e-mail to vegard at vegard dot net with your input. You can also use any of the other points of contact listed on the About page.

Caution

It looks like you're using Google's Chrome browser, which records everything you do on the internet. Personally identifiable and sensitive information about you is then sold to the highest bidder, making you a part of surveillance capitalism.

The Contra Chrome comic explains why this is bad, and why you should use another browser.