Java 8 Top Tips

I’ve been working a lot with Java 8 code over the last couple of years, for both new applications and migrating existing ones, and it feels like the right time to write down some of the “best practices” I’ve found useful. I personally dislike the term “best practices” as it implies a “one size fits all” solution, and of course coding doesn’t work that way – it’s down to us as developers to figure out what will work in our situation. But I have discovered I have particular preferences for Java 8 code that I find makes my life a little easier, and I thought I’d start a discussion on this topic.

Optional

Optional is a highly underrated feature, and one that has the potential to remove a lot of those NullPointerExceptions that can plague us. It’s particularly useful at the boundaries of code (either APIs you’re using or APIs you’re exposing) as it allows you and your calling code to reason about what to expect.

However, applying it without some thought and design can lead to one small change impacting a large number of classes, and can lead to poorer readability. Here are some tips on how to use Optional effectively.

Optional should only be used for return types
…not parameters and not fields. Read this blog post to see a pragmatic approach to using Optional. Fortunately, IntelliJ IDEA lets you turn on an inspection to check you’re following these recommendations

Optional Parameter Warning

Optional values should be dealt with in the place they’re encountered.  IntelliJ IDEA’s suggestions will prevent Optional leaking all over your code, so remember you should deal with Optional where you found it and move swiftly on.

Deal with Optional immediately

You should not simply call get()
The strength of Optional is to express that this value might be empty, and allow you to cater for that case. Therefore, it’s important to check if there is a value before doing anything with it.  Simply calling get() without checking isPresent() first is likely to lead to a null pointer at some point. Fortunately, once again IntelliJ IDEA has an inspection to warn you to stick to this.

Get without isPresent

There is probably a more elegant way
The isPresent() combined with get() certainly does the trick…

Simplest use of Optional

…but there are more elegant solutions. You can use orElse to give an alternative value in the case that it’s null

Using Optional orElse

…or you can use orElseGet to tell it which method to call in the case that the value is null. This might seem the same as the above example, but the supplier method will be called only if needed, so if it’s an expensive method, using the lambda will give you better performance.

Using Optional orElseGet

Using Lambda Expressions

Lambda expressions were one of the main selling points of Java 8.  Even if you’re not using Java 8 yet, you’ve probably got basic understanding of them by now. But they are a new way of programming within Java, and it’s not yet obvious what is “best practice”. Here are some guidelines I like to follow.

Keep it Short
Functional programmers may be happy with longer lambda expressions, but those of us who’ve been using only Java for many years might find it easier to keep lambda expressions to a small number of lines. You might even prefer to limit them to just a single line, and you can easily refactor longer expressions into a method.

Extract longer lambda expressions into methods

These may even collapse down into method references. Method references may seem a bit alien at first, but it’s worth persevering with them as they can actually aid readability in some cases, which I’ll talk about later.

Collapse Lambda to Method Reference

Be Explicit
Type information is missing for lambda expressions, so you may find it useful to include the type information for the parameter.

Explicit parameter types for lambda expressionsAs you can see, this can get quite unwieldy.  So I prefer to give my parameters a useful name. Of course, whether you do this or not, IntelliJ IDEA lets you see type information of parameters.

See the parameter types of the lambda

and even the Functional Interface the lambda represents

See the lambda's functional interface

Designing for Lambda Expressions

I think of lambda expressions as being a little like Generics – with Generics, we use them regularly (for example, adding type information to List<> ), but it’s much more rare that we’ll design a method or a class that has a Generic type (like a Person<T>). Similarly, it feels like we’ll pass lambdas around when using things like the Streams API, but it’ll be much more rare for us to create a method that requires a lambda parameter.

However, if you do find yourself in this situation, here are some top tips.

IntelliJ IDEA can help you introduce a functional parameter
This lets you create a parameter where someone will pass in a lambda rather than an Object. The nice thing about this feature is that it suggests an existing functional interface that matches the specification.

Extract functional parameter

Which leads to…

Use existing functional interfaces
As developers become more familiar with Java 8 code, we’ll know what to expect when using interfaces like Supplier and Consumer, and creating a home-grown ErrorMessageCreator (for example) could be confusing, and wasteful. Take a look at the function package to see what’s already available.

Add @FunctionalInterface to your functional interface
If you really do need to create your own functional interface, then tag it as such with this annotation.  It might not seem to do much, but IntelliJ IDEA will show you if your interface doesn’t match the exceptions for a functional interface. It flags when you haven’t specified a method to override:

Functional interface without a method

it flags when you’ve specified too many:

Functional interface with too many methods

and it warns you if you’ve applied it to a class rather than an interface:

Functional interface that's actually a class

Lambda expressions can be used for any interface with a Single Abstract Method, but they can’t be used for abstract classes that meet the same criteria.  Seems illogical, but it is what it is.

Streams

The Stream API is another one of the big selling points of Java 8, and I think we still don’t really know how much this is going to change (or not) the way we code. Here’s a mixed bag of things I’ve found useful.

Line up the dots
I personally prefer to line up my stream operations. You don’t have to, of course, but I’ve found this helps me:

  • see which operations I have, in order, at a glance
  • debug more easily (although IntelliJ IDEA does offer the ability to set breakpoints on any of multiple lambda expressions on a line, splitting them onto separate lines does make it simpler)
  • comment out an operation while I’m testing things
  • easily insert a peek() for debugging / testing

Line up your dots

Also it’s neater, in my opinion. But we don’t gain a lot in terms of reducing lines of code if we follow this pattern.

You may need to adjust your formatting settings to get the dots lined up.

Formatting Stream method chaining

Use Method References
Yes, it does take a while to get used to this weird syntax. But when used correctly, it really adds to the readability. Consider

Stream using a simple logical filter

verses the use of the helper methods on the (fairly) new Objects class:

Stream with method reference

This latter code is much more explicit about which values it wants to keep.  IntelliJ IDEA will usually let you know when a lambda can be collapsed to a method reference.

Use method reference instead of lambda expression

When iterating over a Collection, use the Streams API where possible
…or the new collections methods like forEach.  IntelliJ IDEA can suggest this to you:

Replace iteration with forEach

Generally using the Streams API is more explicit than a combination of for loops and if statements.  For example:

Using traditional iteration

IntelliJ IDEA suggests this can be refactored to:

Using the Streams API

The performance tests I’ve done show that this sort of refactoring can be surprising – it’s not always predictable whether performance will stay the same, improve, or get worse. As always, if performance is critical in your application, measure it before committing to one style over another.

Use for loops when looping over arrays
However, using Java 8 doesn’t necessarily mean you have to use streams and new collections methods everywhere. IntelliJ IDEA will suggest things that can be converted to streams, but that doesn’t mean you have to say “yes” (remember inspections can be suppressed or turned off).

In particular, iterating over a small array of primitive types is almost definitely going to be better performance with a for loop, and probably (at least while Java developers are new to streams) more readable.

For loops are faster over arrays

As with any of the tips, this rule is not set in stone, but you should decide whether you are going to prefer using the Streams API where you can, or whether you’re still going to use loops for some operations.  Above all, be consistent.

Finally

I’ve written a similar but complimentary post on this topic for the Upsource blog, containing these tips and more things to look out for in Java 8 code when you’re performing a code review.

I’m discovering new things every day, and sometimes my preferences change – method references, for example, I used to hate, and avoided having them in my code. I’d love to hear your top tips!

This entry was posted in Tips & Tricks and tagged , . Bookmark the permalink.

30 Responses to Java 8 Top Tips

  1. Very nice blogpost! Even though I already knew most of the techniques shown here (because I’m always interesten in refactoring my code for new java-versions) I feel now reassured about how I work with those new techniques and I started “extracting methods” for my really long lambda-expressions.

    Thank you very much!

  2. Adam Carroll says:

    A useful set of tips. Thanks for that!

  3. Lorenzo says:

    They look very simple for someone coming from a Scala or C# background, but they are a good read for a pure Java dev. I would add a couple of points on closing over variables and consuming/collecting stream. The last one caught some of my colleagues off balance the first time.

  4. Dusan says:

    Trisha, any thoughts about decision that checked exceptions need to be handled inside lambdas?

    • Trisha Gee says:

      Exceptions inside lambdas are…. well… a bit messy. I don’t necessarily want to defend or criticise the decisions the language developers made around how to handle them in Java, particularly as I think checked exceptions are (generally) not “Best Practice” for a lot of people these days, and yet the language designers need to be able to handle stuff that’s been around in Java since the beginning.

      From my own experience, I haven’t had to handle exceptions much in the lambda expressions that I’ve used, maybe because I prefer runtime exceptions in my code, or because I frequently extract methods for my lambda expressions. But it seems to me that Stephen Colebourne, who works a lot with Java 8, has pragmatic advice on this.

      • Thanks for your response
        Anyway, I find this wrapping & converting checked to runtime exceptions just overhead around strange decision to obligate developers to catch checked exceptions in lambdas. And yes, nowadays we are adviced not to throw checked exceptions, and now we need to handle them in lambdas. Strange :)

  5. Fernando Damian Lopez says:

    Great post! Thanks for sharing!

  6. shashikant fayke says:

    very useful.

  7. Michael says:

    Not using Optional as a parameter to a method seems to be totally opinion based, I have yet to see a reasoning behind that opinion. If you have an API method who takes an optional parameter why wouldn’t you use Optional?

    • TryIO says:

      What’s the point for using Optional as method parameter? Optional is for handling null-cases, not for saying «I pass a null or non-null object» since Optional can be null as well. For instance:

      public void compute(Optional foo) {
      if (foo.isPresent()) …
      }

      it doesn’t solve anything, since if we .compute(null)… boom! yeah you can avoid it by adding:

      public void compute(Optional foo) {
      if (Objects.isNull(foo)) return;

      }

      but this is the worst solution ever, who wants to do something like that when you can:

      public void compute(Foo foo) {
      Optional.ofNullable(foo)
      .ifPresent(value -> …);
      }

      for almost the same reason you don’t want to use Optional for modelling. For instance someone said Optional is the same to say String|Nullable, well it’s not, since String is already Nullable, what’s the point to say String|Nullable|Nullable? What we really need to say is String _must not_ be Nullable, but we need to wait for value objects for that or just implement some primitive obsession pattern.

      • Daniel Wegener says:

        Optional is for handling Optional cases and null has been abused for years to model optional cases. Optional makes this “effect” visible in the type signature – much better than any Annotation could.

        There are arguments about object allocation costs – but most of them are actually about GC stress – which is not really a problem once escape analysis kicks in.

        If somone passes a null for an Optional parameter, its their own fault and they deserve their NPEs IMO. Just stop accepting and dealing with the null.

        I really do not like this warning to be enabled by default!

    • Trisha Gee says:

      I personally wouldn’t create a method that takes an Optional as a parameter for two reasons:

      1) There is a non-negligible performance cost for object creation, so forcing callers of the method to wrap everything in an optional just so I can check whether the value is null or not is potentially expensive
      2) And probably more compelling to me personally, is the ugliness of having to wrap params in an Optional – it’s kinda cruel to force everyone who calls my method to wrap the param in an Optional.

      Consider the example of a method that looks like this:

      to call this method, I need something like:

      or

      whereas I could be much kinder to my callers, by performing the null check on the parameter instead:

      and then my callers have a much easier job:

      or

      My onMessage method looks fairly similar either way, but by opting to NOT take the Optional param but perform the null check, my calling code is much simpler (and also better performance).

  8. Michael Lee says:

    In “Keep it Short” section, which library is it “.until( () -> foundTenItems() );” I am trying to understand how you useful “until” that takes a predicate that takes no argument.

  9. Rubin says:

    I am looking at the code, and can’t believe what an uglienes Java is. And i cant believe that people are still using Java in 21st century 2016. And there are a lot of new cool technologies….

    • John Smith says:

      That’s fine. We are making a living supporting Java applications. The fact that you choose not to leaves more work for us! :)

  10. Alan says:

    In my experience, IDEA is actually pretty bad at telling whether it’s safe to call .get() on an Optional. It works when the isPresent() check is very close to the call, but seems to get lost easily, to the point where I’ve found the IDEA inspection happens so often on code I know is safe that it’s worthless.

    As for Optionals as parameters, the biggest argument for is that Optionals are much cleaner to work with than nulls. Optional.map or Optional.ifPresent (when used with small lambdas, or even better, method references) produce cleaner code (IMO, obviously) than if statements around Optional.isPresent.

    • Trisha Gee says:

      It’s true, IntelliJ IDEA isn’t always perfect at picking up the isPresent/get combo. But given that using get is a bit of a smell anyway, you’re right in saying it’s just better to use things like ifPresent etc.

      I’ve added a link to the end of this post to my (very similar) blog post for Upsource, which contains more tips for using Optional.

  11. Iurii Boiko says:

    Actually for me usage of Optional as parameter looks preferable to a isPresent/get construction when my method has a Optional return type.
    I think the most elegant way of using Optionals is using map/flatMap methods in them. Like:
    java
    Optional process(Optional message) {
    return message.map(tweetMood -> {
    // do something
    });
    }

    • Trisha Gee says:

      Sure, but the point to consider here is not what it looks like in your method, because as you correctly demonstrate it’s very readable. The problem is that all the code that calls your method has an ugly, and potentially expensive, additional wrapping operation, which I demonstrate in the Upsource version of this blog post.

      You are correct that getting the hang of using map and flatMap with Optionals is definitely the key to using them in a readable and powerful way, my feeling at this time is that many of us Java developers haven’t got used to this style of programming yet.

  12. Simon says:

    Do you have any thoughts about choosing to use a method reference vs. defining a lambda expression earlier in the method?
    e.g.
    Callable foundTenItems = () -> { … };
    then later:
    .until (foundTenItems)

  13. David Kerr says:

    Nice article. I prefer to use Optional for both parameters and returns as, for me, it communicates the contract (I like the idea of design by contract), saying if a parameter is not Optional that a value is REQUIRED. If client code passes a null then it can expect NPE’s. Again, if a parameter is Optional and client code still passes null, then I think an NPE is fair game.

    An alternative is to use a ‘better’ language such as RedHat’s Ceylon (https://ceylon-lang.org), JetBrains Kotlin (https://kotlinlang.org) which both build handling optionals VERY NICELY at the language level.

  14. Marcos says:

    The Optional also solves the problem of ambiguity of null values. Previously, developers use the value of null to represent different types of state.

  15. Evgeny says:

    Hi!

    I am doing migration of my project to use Stream api, and when I use “Replace with collect fix” my code turns into one line something like
    List objIds = keys.stream().map(Key::getId).collect(Collectors.toList());

    Can I get it automatically formatted by Intellij? It would be cool if gets formatted after applying this fix, but even if I do “Reformat Code” nothing changes.

    I am hoping to get this kind of code:

    List objIds = keys.stream()
    .map(Key::getId)
    .collect(Collectors.toList());

    • Trisha Gee says:

      Yes, that’s exactly what happens when you choose the settings that I show under the “Line up the dots” section above

Leave a Reply

Your email address will not be published. Required fields are marked *