Java is now moving away from their two to three-year release cycles to a more predictable and fast-paced six-month one, which means there are going to be new and cool features to look forward to much more frequently! For most developers, that translates to an optional but major upgrade every six months. Although Project Jigsaw (Java Platform Module System) is one of the most celebrated and high-profile features of this release, I decided to look into some other make-your-life-easier code/API changes:

1) Streams

The Streams API was one of the biggest features of Java 8, and Java 9 adds some improvements.

dropWhile()

Stream.of(1, 3, 8, 10, 2).dropWhile(n -> (n*10) <50).forEach(System.out::println)
Output: 8, 10, 2

dropWhile()evaluates the elements provided by the stream against the predicate provided ((n x 10)<50 in this case), and drops those elements that satisfy the predicate. Note that the dropWhile() will evaluate the predicate provided until it finds the first number that satisfies the predicate, after which it outputs all the consecutive elements of the stream. This can be observed by the fact that 2 is a part of the output (and was not dropped even though 2 x 10<50).

takeWhile()

Stream.of(1, 3, 8, 10, 2).takeWhile(n -> (n*10)<50).forEach(System.out::println)
Output: 1, 3

takeWhile() is just the opposite of dropWhile() in that it uses the predicate provided and takes those elements that satisfy the predicate. takeWhile() evaluates the predicate provided for all the elements until it finds the first one that does not evaluate to true. Thus 2 is not seen in the output provided because 8 (that is present before 2 in the stream) evaluates to false.

iterate()

Stream.iterate(10, i -> i<=50, i -> i+10).forEach(System.out::println)
Output: 10, 20, 30, 40, 50

Stream.iterate(10, i -> i+10).forEach(System.out::println)
Output: 10, 20, 30, 40, 50, 60, 70, 80, …..

iterate() serves as a stream-version replacement of traditional for-loops. iterate() provides two versions: one that takes in an end condition, thus producing a finite stream, and the other that does not take in an end condition, thus producing an infinite stream. One could use limit(), dropWhile(), or takeWhile() to terminate the infinite stream.

Optional Support

Stream.ofNullable(null) => Produces an empty stream

Stream.ofNullable(8) => Produces a stream with one element (8)

Optionals support was another widely celebrated Java 8 feature, and Java 9 has extended this support to Streams!

Join our developer community!

2) Factory methods for Collections

Java 9 provides convenient, one-line factory methods to create Java collections.

Stream<Integer> numbersStream = Stream.of(10, 5, 7)

Creates a Stream with three elements: 10, 5, 7

List<Integer> numbersList = List.of(10, 5, 7)

Creates a List with three elements: 10, 5, 7

Set<Integer> numbersSet = Set.of(10, 5, 7)

Creates a Set with two elements: 10, 5, 7

Map<Integer, String> numberMap = Map.of(10, “ten”, 5, “five”, 7, “seven”)

Creates a Map that contains 10, 5, 7 as keys and “ten”, “five”, “seven” as their respective values

3) Private methods in Interfaces

Java 8 introduces method implementations in interfaces if the keyword “default” was used. Like other interface methods, the default method has to be public. JDK 9 allows interfaces to have private methods, which in turn could be used by the default methods in the interface to pull up common code.

public interface Shape {

public default foo() {
bar();
System.out.println("Foo");
}

private void bar() {
System.out.println("Bar");
}
}

Private interface methods could be used for the purposes of logging, validation, etc.

4) Optionals

Optionals, that provide improved null management have three new APIs in Java 9.

ifPresentOrElse()

Optional.ofNullable(null).ifPresentOrElse(i -> 
System.out.println("Value Provided " + i), ()-> 
System.out.println("No Value"));

Output: “No Value”

Optional.ofNullable(15).ifPresentOrElse(i -> 
System.out.println("Value Provided " + i), () -> 
System.out.println("No Value"));

Output: “Value Provided 15”

This API takes in a Consumer and a Runnable as input. The Consumer provides the code to be executed if the Optional provides a non-null value, while the Runnable is executed if the Optional contains a null value.

or()

Optional.ofNullable(null).or(() -> Optional.of(2));

Output: Optional[2]

Optional.empty().or(() -> Optional.of(2));

Output: Optional[2]

 Optional.ofNullable(10).or(() -> Optional.of(2));

Output: Optional[10]

or() takes in Supplier that provides a default optional value to be used in case the Optional contains a null or is empty. Note that the default value (which is 2 in this case) would need to be wrapped in an Optional as shown.

stream()

Optional.ofNullable(null).stream()
Output: Creates an empty stream

Optional.ofNullable(20).stream()
Output: Creates a stream with one element  (20)

This feature could be used to conveniently combine Optional streams by downstream methods.

5) Reactive Support

Reactive paradigm has taken the programming world by storm, so it’s no surprise that Java provides its own implementation of the Reactive spec. JDK 9 introduces Flow, which contains a Publisher, Subscriber, Subscription, and Processor. The Publisher is the source of data that flows through to the Subscriber via a Subscription. The Processor is a convenience unit that sits in between the Publisher and Subscriber and can be used to process the data. Below is a very simple example (with no error handling) to illustrate how these four items could be wired up together:

Publisher

Publisher<Integer> p = new Publisher<Integer>() {
@Override
public void subscribe(Subscriber<? super Integer> subscriber) {
   System.out.println("Publisher publishes: " + 15);
   subscriber.onNext(15);
 }
};

Subscriber

Subscriber<Integer> s = new Subscriber<Integer>() {
@Override
public void onSubscribe(Subscription subscription) {
   subscription.request(1);
}

@Override
public void onNext(Integer item) {
   System.out.println("Subscriber received: " + item);
}

@Override
public void onError(Throwable throwable) {

}

@Override
public void onComplete() {

}

};

Subscription

pub.subscribe(sub)

Subscription is the means through which subscribers can subscribe to the data published by a publisher. Adding the Subscription will yield the following output:

Publisher publishes: 15
Subscriber received: 15

Processor
The processor can request data from the Publisher via a Subscription, process that data and deliver it to the Subscriber via another Subscription.

Processor<Integer, Integer> pro = new Processor<Integer, Integer>() {

Optional<Integer> item;

@Override
public void subscribe(Subscriber<? super Integer> subscriber) {
   item.ifPresent(i -> {
       System.out.println("Processor publishes: " + i * 20);
       subscriber.onNext(i * 20);
   });
}

@Override
public void onSubscribe(Subscription subscription) {
    subscription.request(1);
}

@Override
public void onNext(Integer item) {
   System.out.println("Processor received: " + item);
   this.item = Optional.ofNullable(item);
}

@Override
public void onError(Throwable throwable) {

}

@Override
public void onComplete() {

}
};

pub.subscribe(processor);
processor.subscribe(sub);

Adding a processor along with the necessary Subscriptions would give us the following output:

Publisher publishes: 15
Processor received: 15
Processor publishes: 300
Subscriber received: 300

Further reading and examples:

Interested in getting more involved in our development community? Sign up to be a part of CoLabs, our community where participants can focus on:
  • Continued learning
  • Meetups
  • Happy hours
  • Job opportunities

Learn more about CoLabs!