Tuesday, December 26, 2017

Java 9: JShell

JShell is a new tool introduced in Java 9 that evaluates Java statements entered on the command line. It is the first offical REPL (Read-Evaluate-Print Loop) implementation for the Java platform, and it is great for trying out Java code without having to fire up an IDE or write a full program!

To run JShell, simply type jshell on the command line. Obviously, make sure that you have installed JDK 9 and that your JAVA_HOME environment variable is set correctly. You will see a prompt like this:

$ jshell
|  Welcome to JShell -- Version 9
|  For an introduction type: /help intro

jshell>

Type /help at the prompt to see a list of available commands. To exit, type /exit.

You can enter code "snippets" and JShell will output the results. For example:

jshell> System.out.println("Hello World")
Hello World

You can auto-complete statements and also look at documentation using the Tab key:

jshell> System.out.
append(        checkError()   close()        equals(        flush()        format(        getClass()     hashCode()     notify()
notifyAll()    print(         printf(        println(       toString()     wait(          write(

Here is a screencast GIF showing JShell in action, created using LICECap:

Monday, December 25, 2017

Java 9: Enhancements to the Stream API

Java 9 adds 4 new methods to the Stream interface:

1. dropWhile

The dropWhile method is similar to the skip method but uses a Predicate instead of a fixed integer value. It drops elements from the input stream while the Predicate is true. All remaining elements are then passed to the output stream. For example:

IntStream.range(0, 10)
         .dropWhile(i -> i < 5)
         .forEach(System.out::println);
// prints 5, 6, 7, 8, 9

2. takeWhile

The takeWhile method is similar to the limit method. It takes elements from the input stream and passes them to the output stream while the Predicate is true. For example:

IntStream.range(0, 10)
         .takeWhile(i -> i < 5)
         .forEach(System.out::println);
// prints 0, 1, 2, 3, 4
Note: Be careful when using dropWhile and takeWhile when you have an unordered stream because you might get elements in the output stream that you don't expect.

3. ofNullable

The ofNullable method returns an empty stream if the element is null, or a single-element stream if non-null. This eliminates the need for a null check before constructing a stream.

Stream.ofNullable(null).count();  // prints 0
Stream.ofNullable("foo").count(); // prints 1

4. iterate

The static iterate method has been overloaded in Java 9 to allow you to create a stream using the for-loop syntax. For example:

Stream.iterate(0, i -> i < 10, i -> i + 1)
      .forEach(System.out::println); //prints from 0 to 9

Java 9: Enhancements to Optional

Previously, I wrote about the Optional class that was introduced in Java 8 to model potentially absent values and reduce the number of places where a NullPointerException could be thrown.

Java 9 adds three new methods to Optional:

1. ifPresentOrElse

The new ifPresentOrElse method allows you to perform one action if the Optional is present and a different action if the Optional is not present. For example:

lookup(userId).ifPresentOrElse(this::displayUserDetails,
                               this::displayError)

2. stream

The new stream method makes it easier to convert a stream of Optional objects into a stream of values that are present in them. Previously (in Java 8), you needed two steps in order to achive this. First, you would filter out the empty Optionals and then you would unbox the rest in order to get their values. This is shown below:

// In Java 8:
Stream.of("alice", "bob", "charles")
      .map(UserDirectory::lookup)
      .filter(Optional::isPresent)
      .map(Optional::get)
      .collect(toList());

In Java 9, the code becomes simpler using the stream method:

// In Java 9:
Stream.of("alice", "bob", "charles")
      .map(UserDirectory::lookup)
      .flatMap(Optional::stream)
      .collect(toList());

3. or

The or method is somewhat similar to the orElseGet method but returns Optional objects instead of values. If a value is present, it returns the existing Optional. If the value is not present, it returns the Optional produced by the supplying function. For example:

lookup(userId).or(() -> lookupInAnotherDatabase(userId));