5 hidden gems in Java 8

There’s been a lot of attention for the major new features of Java 8: Lambdas, the streaming API and the new Date and Time API. Of course these are the ones that make this a game changing release, but there’s more to Java 8. Inspired by José Paumard´s Devoxx talk 50 new things we can do with Java 8 I’d like to shed some light on 5 smaller features that will make your life as a Java developer easier. I won’t go 50 like José (and actually… neither did he), but these are the ones you definitely need to see.

Join me!

So we probably all had our fair share of recreating the same boilerplate code over and over again: iterating over a list of values in order to concatenate them with a delimiter to a single String. In Java 7 this would probably look something like this:

List<String> values = ...
StringBuilder result = new StringBuilder("[");
boolean first = true;
for(String item : values) {
  if(first) {
    first = false;
  } else {
    result.append(", ");
  }
  result.append(item);
}
result.append("]");
System.out.println(result.toString());

While this is certainly more concise and more readable than how the code would have looked in the 1.4 era, before generics and enhanced for-loops, but it still is a hideous pile of boilerplate for something very simple. So now for the Java 8 solution:

StringJoiner joiner = new StringJoiner(", ", "[", "]");
values.forEach(joiner::add);
System.out.println(joiner.toString());

This actually showcases not only the new StringJoiner, but two other Java 8 features as well: method references and the forEach() method on the Iterable interface.

While StringJoiner is actually meant for some behind-the-scenes processing for a Collector in the streaming API (http://blog.joda.org/2014/08/stringjoiner-in-java-se-8.html), it does eliminate a lot of boilerplate in more traditional code.

Longing for hashCode

When implementing your own hashCode() method for a class that has long fields, in the past you had to calculate the hash yourself or wrap the long value in a Long object and then call its hashCode() method. In order to avoid unnecessary creation of objects, Java 8 allows you to use the static method Long.hashCode(long value) for this.

Line it up!

Java 7 gave us the very convenient Files class with a lot of useful static helper methods for working files, amongst which Files.readAllLines(Path path). This class is also updated to make the best use of the Streaming API. So now we get the even more useful Files.lines(Path path), which does not return a List of all the lines, but a Stream. This is probably a better programming model in almost all cases. When you read all the lines in a file, you will probably want to process them somehow instead of keeping them in memory. Below an example of reading all the lines in a file and printing out only those lines that start with an “A”.

Path file = Paths.get("path", "to", "file.txt");
try (Stream<String> lines = Files.lines(file)) {
  lines
    .filter(s -> s.startsWith("A"))
    .forEach(System.out::println);
} catch (IOException ioe) {
  // ...
}

Repeat after me

A new feature that will probably find most of its use in the Java EE world, is @Repeatable. By annotating your annotation type with the meta-annotation @Repeatable, it can be placed at the same element more than once. Under the hood this still wraps the separate annotations in a container annotation, but it reads a lot better.

Since the annotation is not used within Java SE 8 itself, there are only a lot of imaginary examples circulating on the internet. But then again this feature was introduced with Java EE in mind. So below snippet (derived from the Java EE 7 spec) will likely be a valid JAX-B example in Java EE 8:

public class Foo {
  @XmlElement(name="A", type=Integer.class)
  @XmlElement(name="B", type=Float.class)
  public List items;
}

Which will below the surface be translated to the current notation:

public class Foo {
  @XmlElements(
    @XmlElement(name="A", type=Integer.class),
    @XmlElement(name="B", type=Float.class)
  )
  public List items;
}

By default

Default methods on interfaces are often presented as a by-product of Lambda’s, but they can make it a lot easier to create a sustainable future-proof API. A default method is a method on an interface of which the (default) implementation is already provided.

Say for instance your public API exposes the following interface:

public interface Foo {
  void addListener(FooListener listener);
}

Say for instance that you want to add the possibility to add multiple listeners in one go without breaking the implementations of your customers. This can be achieved by adding a default method:

public interface Foo {
  void addListener(FooListener listener);

  default void addListeners(Collection<FooListener> listeners) {
    listeners.forEach(this::addListener);
  }
}

And many more

While I stick to five here, there are many, many more additions to Java 8. You can find the full list here: http://www.oracle.com/technetwork/java/javase/8-whats-new-2157071.html.

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

About jaapcoomans