In the year 2021, Java turned 26 years old.
Yes, Java is a pretty old programming language. Still, it holds popularity in the software development landscape and is one of the widely used programming languages across the globe.
Source: TIOBE Index
Source: PYPL
The majority of Fortune 500 enterprises use this programming language. The Java language is making enhancements periodically to be in the market for a longer time.
Java 9 has transformed the way Java developers write code to develop Java-based applications. It has come up with lots of improvements to existing APIs.
We will be discussing the following listed API improvements in Java 9:
Let's deep dive into the API improvements of Java 9.
In Java, streams were introduced to assist Java application developers to perform aggregate operations from a collection of objects by using various methods. In order to improve stream API, Java 9 comes with some new methods.
Let’s talk about the new methods added to the stream interface in Java 9 to improve the stream API.
a) Java Stream Iterate Method
The iterate method is introduced to the Java 9 stream interface. It allows you to iterate stream elements till the given condition. The iteration of stream elements stops as soon as the predicate (condition) returns false.
This method takes three parameters- seed, hasNext and next.
seed: Indicates an initial element.
hasNext: It is a condition (predicate) that applies to elements to verify when the stream must end.
next: It is a function to be applied to the previous stream element to generate a new stream element.
import java.util.stream.Stream;
public class StreamExample
{
public static void main(String[] args)
{
Stream.iterate(1, i -> i <= 10, i -> i\*3)
.forEach(System.out::println);
}
}
Output: [1, 3, 9]
b) Java Stream dropWhile() Method
Stream dropWhile method gives the outcome based on the order of the stream elements.
Ordered stream: In an ordered stream, the dropWhile()
method returns a stream that consists of elements after removing the elements that match the specified predicate (condition).
Unordered stream: In an unordered stream, this method returns a stream that consists of leftover elements of this stream after removing a subset of elements that match the specified predicate.
Implementation of Stream dropWhile() Method:
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;
public class StreamExample {
public static void main(String[] args) {
List<Integer> list
= Stream.of(2,2,3,4,5,6,7,8,9,10)
.dropWhile(i -> (i % 2 == 0)).collect(Collectors.toList());
System.out.println(list);
}
}
Output: [3, 4, 5, 6, 7, 8, 9, 10]
c) Java Stream takeWhile() Method
Java 9 comes with the Stream takeWhile method to improve the stream API in Java. The method returns every element until the predicate (condition) unmatched the elements. And the program ends just when it fails to satisfy the predicate.
-Implementation of Stream takeWhile() Method:
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;
public class StreamExample {
public static void main(String[] args) {
List<Integer> list
= Stream.of(1,2,3,4,5,6,7,8,9,10)
.takeWhile(i -> (i % 2 == 0)).collect(Collectors.toList());
System.out.println(list);
}
}
Output: []
Here, takeWhile()
method returns an empty list because the condition is not matched at first list element, and the method terminates here.
-Implementation of Stream takeWhile() Method:
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;
public class StreamExample {
public static void main(String[] args) {
List<Integer> list
= Stream.of(2,2,3,4,5,6,7,8,9,10)
.takeWhile(i -> (i % 2 == 0)).collect(Collectors.toList());
System.out.println(list);
}
}
Output: [2,2]
Here, the method returns the first two elements because the condition matched for the first 2 elements (even elements), and the third element failed to satisfy the condition. So, the program ends at the third element.
d) Java 9 Stream ofNullable Method
Prior to Java 9, you couldn’t make a stream using a null value, it would throw NullPointerException error.
To handle this error, Java 9 has added a Stream ofNullable method to the stream interface. This method returns a sequential stream that consists of a single element if the parameter of the method is non-null. And the method returns an empty stream for a null parameter.
Implementation of Stream ofNullable Method:
import java.util.stream.Stream;
public class StreamExample {
public static void main(String\[\] args) {
Stream<Integer> val
= Stream.ofNullable(null);
val.forEach(System.out::println);
}
}
This code will not give any result.
The process API in Java helps in managing and controlling Operating system processes. In the older versions, managing OS processes was a cumbersome task to do for developers. With the advent of Java 9, the process API has improved a lot.
In Java 9, some new classes and interfaces are introduced for interacting and managing OS.
Two new interfaces have been introduced to the JDK:
Java ProcessHandle Interface
ProcessHandle interface helps developers to manage and control processes. With a slew of static factory methods in this interface, you can monitor every process for aliveness, list its children, process ID, current direct children of the process, the killing of the process, and many more.
Signature of Java ProcessHandle Interface
public interface ProcessHandle extends Comparable<ProcessHandle>
Java ProcessHandle.Info Interface
Java 9 has introduced this new interface to give information about the processes. This newly added interface is the nested interface of the ProcessHandle interface.
Signature of Java ProcessHandle.Info Interface
public static interface ProcessHandle.Info
We have tabled various methods in ProcessHandle.Info interface.
Java 9 brings a new multi-resolution image API that supports multiple images with different image resolution variants. This Java 9 API allows a group of images with different resolutions to be used as a single multi-resolution image.
We have listed down the important methods of multi-resolution images.
Image getResolutionVariant(double destImageWidth, double destImageHeight) −
Gets a specific image which is the best variant to represent this logical image at the indicated size.
List<Image> getResolutionVariants()
– This method returns a readable list of all resolution variants.
A CompletableFuture is a type of class in Java that belongs to java.util.concurrent package. This class implements CompletionStage and Future interface. It was introduced in Java 8.
Java developers use this class for asynchronous programming which means to write non-blocking code.
It runs a task on a thread other than the main application thread. And also sends a notification to the main thread about its task status whether the task is in progress, completed, or failed.
In this way, the main thread does not wait for the task to get over. Lots of other tasks run in parallel. This helps in enhancing the performance of an application.
Java 9 brings some enhancements to the CompletableFuture API.
Optional Class is a public final class in Java. This class was introduced in Java 8 to avoid null checks and NullPointerException issues. Java developers need to import java.util package to use the Optional class in your applications. This class provides methods to check whether a value is present for a particular variable or not.
Java 9 brings 3 new methods to enhance its functionality. Let’s have a look at these methods.
public Stream<T> stream()
If there is any value, it will return a sequential Stream consists of that value, otherwise returns an empty Stream.
ifPresentOrElse()
public void ifPresentOrElse(Consumer<? super T> action, Runnable emptyAction)
If there is any value, this method will perform the given task with the given value, otherwise performs the given empty-based action.
or()
public Optional<T> or(Supplier<? extends Optional<? extends T>> supplier)
If there is any value, the method will return an Optional describing the value, otherwise returns an Optional generated by the supplying function.
Previously, for creating a list of 5 elements, developers used to write the following code.
import java.util.ArrayList;
import java.util.List;
public class FactoryMethodsExample {
public static void main(String[] args) {
List<String> list = new ArrayList<>();
list.add("Java");
list.add("JavaFX");
list.add("Spring");
list.add("Hibernate");
list.add("JSP");
for(String l : list){
System.out.println(l);
}
}
}
Output:
Spring JavaFX JSP Java Hibernate
As you can see, in the above-written code, add method is called frequently for each list element.
Here comes the improvised version of doing the same task. In Java 9, you can do it in a better way with the help of collection factory methods.
So, what are these Factory methods?
Well, Factory methods are a special kind of static method that assists developers to make unmodifiable instances of collections in Java. By the collection Factory methods, you can make list, set, and map a small number of elements.
Signature of Java 9 Set & List Interface Factory Methods
Based on the number of parameters, it will return an immutable set of elements.
static <E> Set<E> of() static <E> Set<E> of(E e1) static <E> Set<E> of(E e1, E e2)
// ....and so on
It returns an immutable set comprised of a random number of elements. static <E> Set<E> of(E... elements)
Similarly, for List interface:
static <E> List<E> of() static <E> List<E> of(E e1) static <E> List<E> of(E e1, E e2)
// ....and so on
static <E> List<E> of(E... elements)
Generally, 10 elements are enough. In case, more than 10 are needed, you can make use of the var-args version.
But you might be thinking, what is the use of 11 overloading methods if you have a var-args version that can return n number of elements.
Well, the performance factor is the answer to your query.
Every var-args method call implicitly makes an array. While overloading methods do not create objects unnecessary and thus avoid garbage collection overhead.
Implementation of Java 9 Set Interface Factory Method:
import java.util.Set;
public class FactoryMethodsExample {
public static void main(String[] args) {
Set<String> set = Set.of("Java","JavaFX","Spring","Hibernate","JSP");
for(String l:set) {
System.out.println(l);
}
}
}
Output:
Spring JavaFX JSP Java Hibernate
Java 9 Map Interface Factory Methods
Map Interface
In Java 9, Map interface has two factory methods- Map.of() and Map.ofEntries() that assist Java developers to build immutable maps seamlessly.
Implementation of Java 9 Map Interface Factory Method:
import java.util.Map;
public class FactoryMethodsExample {
public static void main(String[] args) {
// Creating Map Entry
Map.Entry<Integer, String> e1 = Map.entry(101, "Java");
Map.Entry<Integer, String> e2 = Map.entry(102, "Spring");
// Creating Map using map entries
Map<Integer, String> map = Map.ofEntries(e1,e2);
// Iterating Map
for(Map.Entry<Integer, String> m : map.entrySet()){
System.out.println(m.getKey()+" "+m.getValue());
}
}
}
Output:
102 Spring 101 Java
We have listed the common characteristics of Factory method for set, list, and map instance:
Points to Ponder:
For List and Set interfaces, of() method is overloaded to take 0 to 10 parameters and one with var args parameter.
For Map interface, of() method is overloaded to have 0 to 10 Key-value pairs.
And if there are more than 10 key-value pairs for Map interface, you can use ofEntries() method by taking var args parameter.
Wrapping Up
Hope you have learned about the API improvements in Java 9. These API improvements will assist you to write better code for your Java-based application development projects.
Happy learning!