What’s new in java 9 for developers
Now that Java 9 is Features Complete, it’s time to look at all the new stuff that this new version will bring us, developers using java.
Of course, everybody have heard about the modularization of the JDK (project Jigsaw), a long awaiting project. Well, I’m not going to talk about it here! I will only speak about the new functionalities that target common developers, not the ones that target framework authors or advanced users.
First of all, the development of Java 9 was mainly made via a set of JEP (Java Enhancement Proposal, more info here) : http://openjdk.java.net/jeps/1) that we can found the list here : http://openjdk.java.net/projects/jdk9/. Of all these JEPs, I will talk about the ones that contains changes visible and usable in the everyday life of a developer.
JEP 102: Process API Updates
Managing from java the call to an external process has always been complicated. Especially if we want to do some operations that seems basic but not foreseen in the actual implementation (getting the PID, killing a process, gathering the command lines, …)
Via the JEP 102 : Process API Updates, the Process API have been greatly enhanced to allow all these information, on the process of the JVM (ProcessHandle.current()), or on a child process created by Runtime.getRuntime().exec(“your_cmd”). Moreover, like always with java, it’s compatible will all the supported OS (so with Windows AND Linux!).
Here are some usage examples in Java 9:
// Get PIDs of own processes System.out.println("Your pid is " + ProcessHandle.current().getPid()); //start a new process and get PID Process p = Runtime.getRuntime().exec("sleep 1h"); ProcessHandle h = ProcessHandle.of(p.getPid()).orElseThrow(IllegalStateException::new); // Do things on exiting process : CompletableFuture ! h.onExit().thenRun( () -> System.out.println("Sleeper exited") ); // Get info on process : return Optional! System.out.printf("[%d] %s - %s\n", h.getPid(), h.info().user().orElse("unknown"), h.info().commandLine().orElse("none")); // Kill a process h.destroy();
JEP 110: HTTP/2 Client (Incubator)
HTTP/2 is already out and a lot of websites already use it. So it is time for Java to offer a client implementation. In the mean time, the API has been revisited to offer a simpler implementation that the really old HttpURLConnection that was the only way to make HTTP request in Java in the JDK (and the reason why developers used third-party library like Apache HttpClient or OKHttp). With the JEP 110: HTTP/2 Client (Incubator) Java get an up-to-date HTTP client, synchronous or asynchronous (in this case, it is based on CompletableFuture).
Here is an example of an asynchronous usage :
/** * The HTTP API functions asynchronously and synchronously. In asynchronous mode, work is done in threads (ExecutorService). */ public static void main(String[] args) throws Exception { HttpClient.getDefault() .request(URI.create("http://www.loicmathieu.fr")) .GET() .responseAsync() // CompletableFuture .thenAccept(httpResponse -> System.out.println(httpResponse.body(HttpResponse.asString())) ); Thread.sleep(999); // Give worker thread some time. }
JEP 259: Stack-Walking API
Going through an execution stack in Java has never been an easy task … now it is one, thanks to the Stack-Walking API : http://openjdk.java.net/jeps/259
// return class/method only for our classes. private static List<String> walkAndFilterStackframe() { return StackWalker.getInstance().walk(s -> s.map( frame -> frame.getClassName() + "/" + frame.getMethodName()) .filter(name -> name.startsWith("fr.loicmathieu")) .limit(10) .collect(Collectors.toList()) ); }
JEP 269: Convenience Factory Methods for Collections
The JEP 256 brings one of the biggest API change in Java 9 : static methods on the interfaces of the Collection API for easily create immutable collections. More info here : http://openjdk.java.net/jeps/269. This API targets the creation of small collections (list, set, map). Performance and memory utilization has been on the center of the implementation of these collections.
List<Integer> listOfNumbers = List.of(1, 2, 3, 4, 5); Set<Integer> setOfNumbers = Set.of(1, 2, 3, 4, 5); Map<String, String> mapOfString = Map.of("key1", "value1", "key2", "value2"); Map<String, String> moreMapOfString = Map.ofEntries( Map.entry("key1", "value1"), Map.entry("key2", "value2"), Map.entry("key1", "value3") );
JEP 266: More Concurrency Updates
The JEP 266: More Concurrency Updates as it’s name didn’t indicate, contains one of the major evolution of Java 9 : an implementation of the Reactive Streams in Java via the Flow class. Java will then join the very hype group of reactive languages!
The class Flow contains three interfaces for the implementation of your reactive streams :
Publisher :
Publish messages that the subscribers will consume. The only method issubscribe(Subscriber).
Subscriber :
Subscribe to a publisher for receiving messages (via the methodonNext(T)
), error messages (onError(Throwable)
), or a signal that there will be no more messages (onComplete()
). Before anything else, the publisher needs to callonSubscription(Subscription)
.Subscription :
The connection between a publisher and a subscriber. The subscriber will use it for asking for messages (request(long)
) or cutting the connection (cancel()
).
Here is an example from the tutorial available here :
public class MySubscriber<T> implements Subscriber<T> { private Subscription subscription; @Override public void onSubscribe(Subscription subscription) { this.subscription = subscription; subscription.request(1); //a value of Long.MAX_VALUE may be considered as effectively unbounded } @Override public void onNext(T item) { System.out.println("Got : " + item); subscription.request(1); //a value of Long.MAX_VALUE may be considered as effectively unbounded } @Override public void onError(Throwable t) { t.printStackTrace(); } @Override public void onComplete() { System.out.println("Done"); } }
And now the Publisher example :
//Create Publisher SubmissionPublisher<String> publisher = new SubmissionPublisher<>(); //Register Subscriber MySubscriber<String> subscriber = new MySubscriber<>(); publisher.subscribe(subscriber); //Publish items System.out.println("Publishing Items..."); String[] items = {"1", "x", "2", "x", "3", "x"}; Arrays.asList(items).stream().forEach(i -> publisher.submit(i)); publisher.close();
Moreover, there as been some changes on the CompletabeFuture API (it’s more or less an equivalent to the JavaScript promises in Java) that allow, among other things, a better composition of the CompletableFuture between them :
- copy():CompletableFuture
- completeAsync(Supplier< ? extends T> supplier):CompletableFuture
- orTimeout(long timeout,TimeUnit unit):CompletableFuture
- completeOnTimeout(T value, long timeout, TimeUnit unit):CompletableFuture
- failedFuture(Throwable ex):CompletableFuture
For a complete list of these new methods, see the once with Since 9” in the javadoc : http://download.java.net/java/jdk9/docs/api/java/util/concurrent/CompletableFuture.html
JEP 277: Enhanced Deprecation
The JEP 277: Enhanced Deprecation allow to give extra information on the deprecation via the annotation @Deprecated, two new attributes that allow developers to know if the deprecated API used is intended to be deleted some day and when the deprecation occurs. The purpose is to facilitate the lifecycle of applications and allow, maybe more easily, to delete some API on the JDK itself in the future.
Here is a small example that said that the MyDeprecatedClass is deprecated since version 9 and will be deprecated some days :
@Deprecated(since="9", forRemoval=true) public class MyDeprecatedClass { //deprecated stuff }
JEP 222: jshell: The Java Shell (Read-Eval-Print Loop)
A lot of languages (ruby, scala, python, …) have a Read-Evaluate-Print-Loop (REPL), a shell. This allow an easy learning of the language and give a direct access to the language from a simple shell. Whether it was for introducing the language, prototyping or testing, it’s always a good thing to have a shell and avoid the ceremonial of editing, compiling and packaging code.
With Java 9, born jshell, the java REPL! More info on the JEP : http://openjdk.java.net/jeps/222
From the command line, execute
An article that goes into more details : http://jakubdziworski.github.io/java/2016/07/31/jshell-getting-started-examples.html
JEP 213: Milling Project Coin
The Project Coin is a project of evolution of the language, started with Java 7, with the purpose of simplification of the usage of the language for the developers, by bringing small modifications in the order of “syntactic sugar”. The JEP213 : Milling Project Coin contains the implementation in Java 9 of the last parts of the project.
- @SafeVarargs allowed on private method (previously only on static or final ones)
- Allow the operator <> for abstract class (when the type is denotable)
- Disallow ‘_’ as a valid identifier to allow it’s reuse in Java 10 (traditionally used to name an unused parameter that we don’t want to use when overloading a method)
- Private methods in interfaces : allow code factorization in static methods (factorization of codes between two static methods in the same interface)
- Allow final variables (or effectively final) in a try-with-resource (example bellow) :
Before Java 9:
final Resource r = new Resource(); try (Resource r2 = r) { … }
In Java 9 :
final Resource r = new Resource(); try (r) { … // Cannot mutate r }
JEP 211 : Elide Deprecation Warnings on Import Statements
Before : importing a deprecated class generate a warning at compile time, after … no more warning. More info here : http://openjdk.java.net/jeps/211
A lot of other changes, outside any JEP :
Stream & Collectors:
- Stream.takeWhile(Predicate< ? super T> predicate):Stream
: build a stream that contains the element of the first one while the predicate is true, as soone as the predicate becomes false, the stream is stopped. - Stream.dropWhile(Predicate< ? super T> predicate):Stream
: the opposite of takeWhile, build a stream that contains the first false elements then all the others. While the predicate is false : drop the elements, then, include them in the stream. - Stream.ofNullable(T element):Stream
: return a stream with the element or an empty one if the element is null. Avoid using Optional with the streams. - Stream.iterate(T, Predicate< ? super T>, UnaryOperator
) : réplique une boucle for standard : Stream.iterate(0; i -> i<10, i -> i+1) - Collectors.filtering(Predicate< ? super T>,Collector< ? super T,A,R>) : execute a filter prior to the collector (see an example in the Javadoc that explain the differences between Stream.filter() before a collector)
- Collectors.flatMapping(Function< ? super T,? extends Stream extends U>>,Collector< ? super U,A,R>) : execute a flatMap operation prior to the collector (see an example in the Javadoc)
//iterate /java 8 style : using for loop for (int i = 0; i < 10; ++i) { System.out.println(i); } //java 9 style, using Stream.iterate Stream.iterate(0, i -> i < 10, i -> i + 1).forEach(System.out::println); //takeWhile and dropWhile Stream<String> stream = Stream.iterate("", s -> s + "s") stream.takeWhile(s -> s.length() < 10); stream.dropWhile(s -> !s.contains("sssss")); //ofNullable : returning Stream.empty() for null element //java 8 style : we need to make a check to know if it's null or not collection.stream() .flatMap(s -> { Integer temp = map.get(s); return temp != null ? Stream.of(temp) : Stream.empty(); }) .collect(Collectors.toList()); //java 9 style collection.stream().flatMap(s -> Stream.ofNullable(map.get(s))).collect(Collectors.toList());
List<Integer> numbers = List.of(1, 2, 3, 5, 5); Map<Integer, Long> result = numbers.stream() .filter(val -> val > 3) .collect(Collectors.groupingBy(i ->; i, Collectors.counting())); result = numbers.stream() .collect(Collectors.groupingBy(i -> i, Collectors.filtering(val -> val > 3, Collectors.counting()) ));
An article on this suject : http://www.baeldung.com/java-9-stream-api
Optional
4 new methodes have been added to the Optional class :
- or(Supplier):Optional : return the same Optional or one build with the Supplier in parameter if there is no value. This allow a lazy construct of the other Optional is no value is provided.
- ifPresent(Consumer):void : execute the Consumer in parameter if there is a value.
- ifPresentOrElse(Consumer, Runnable):void : execute the Consumer in parameter if there is a value or execute the Runnable
- stream():Stream
: return a Stream of one element if there is a value or an empty Stream. This allow to use the power of the Stream API with Optional
//Optional.or : a lazy version of orElse Optional<String> value = ...; Optional<String> defaultValue = Optional.of(() -> bigComputation()); return value.or(defaultValue);//bigComputation will be called only if value is empty //Optional.ifPresent : Optional<String> value = ...; AtomicInteger successCounter = new AtomicInteger(0); value.ifPresent( v -> successCounter.incrementAndGet()); //Optional.ifPresentOrElse : Optional<String> value = ...; AtomicInteger successCounter = new AtomicInteger(0); AtomicInteger onEmptyOptionalCounter = new AtomicInteger(0); value.ifPresentOrElse( v -> successCounter.incrementAndGet(), onEmptyOptionalCounter::incrementAndGet); //Optional.stream : unify the stream and Optional API Optional<String> value = Optional.of("a"); List<String> collect = value.stream().map(String::toUpperCase).collect(Collectors.toList()); //["A"]
An article on this subject : http://www.baeldung.com/java-9-optional
Java Time :
Multiple addons on the Java Time API, among other things the possibility to create streams of dates with LocalDate.datesUntil(LocalDate) and LocalDate.datesUntil(LocalDate, Period).
More info here : http://blog.joda.org/2017/02/java-time-jsr-310-enhancements-java-9.html
Others :
- InputStream.readAllBytes():byte[] : read in one time an input stream in a byte array
- InputStream.readNBytes(byte[] b, int off, int len):int : read in one time an input stream in a byte array with offset and limit.
- Objects.requireNonNullElse(T obj, T defaultObj) : return the first element if not null, otherwise the second. If both are null : NullPointerException !
- Objects.requireNonNullElseGet(T obj, Supplier supplier) : return the first element if not null, otherwise call supplier.get().
- int Objects.checkIndex(int index, int length) : check the index : generate an IndexOutOfBoundsException if the index is less than 0 or more or equals to the size. This method might be optimized by the JVM.
- int Objects.checkFromToIndex(int fromIndex, int toIndex, int length) : the same but for the sub-range fromIndex/toIndex
- int Objects.checkFromIndexSize(int fromIndex, int size, int length) : the same but for the sub-range fromIndex/fromIndex + size
- {Math, StrictMath}.fma() : implementation of a fused-multiply-accumulate (fma)
- {Math, StrictMath}.{multiplyExact, floorDiv, floorMod}
- {BigDecimal, BigInteger}. sqrt() : square root
- Arrays.equals(), Arrays.compare(), Arrays.compareUnsigned(), Arrays.mismatch( : a lot of new methods to compare arrays with a lot variations : signed, unsigned,
equality, mismatch, with from/to index, …
Performance :
The following JEPs are focused on the performance of the JVM, I will not introduce them in details here (but probably in a next article), here is the list :
- JEP 143: Improve Contended Locking : optimization of Java monitors (locks).
- JEP 193: Variable Handles : C++ atomics …
- JEP 197: Segmented Code Cache : the code cache (a part of the Metaspace) has been segmented to optimize the performances.
- JEP 254: Compact Strings : enhancement of the String implementation in Java to allow a more compact version in case of ISO-8859-1 (or Latin-1) strings. String in Java was stored by defaults in UTF-16 : each character was stored on two bytes. Now, when the string will be created, if it’s compatible with ISO-8859-1 it will be stored on one byte only.
- JEP 274: Enhanced Method Handles : several addons to the MethodHandle API
- JEP 280: Indify String Concatenation : intrasification of the concatenation of the strings on the JVM. Multiple concatenation strategies has been implemented, including one based on Method Handles (with StringBuilder or inline), this is the default one.
- JEP 285: Spin-Wait Hints : for low level (power) users only : this ability to give a hint to the JVM that the application is in a spin-wait-loop …
Links :
Here are the articles that inspires me to write this one :
https://blogs.oracle.com/darcy/resource/Devoxx/DevoxxUS-2017-jdk9-lang-tools-libs.pdf
http://docs.oracle.com/javase/9/whatsnew/toc.htm#JSNEW-GUID-BA9D8AF6-E706-4327-8909-F6747B8F35C5
http://blog.takipi.com/5-features-in-java-9-that-will-change-how-you-develop-software-and-2-that-wont/
https://bentolor.github.io/java9-in-action
http://www.javaworld.com/article/2598480/core-java/why-developers-should-get-excited-about-java-9.html
https://www.sitepoint.com/ultimate-guide-to-java-9/
http://www.javamagazine.mozaicreader.com/JulyAug2017
One thought on “What’s new in java 9 for developers”
Article très intéressant, j’ai vu pas mal de choses que je n’avais pas vu ailleurs dans d’autres articles “What’s new in Java 9…”
Pour infos mes features préférées sont :
* Factory Methods for Collections (hier encore je galérais à initialiser une Map statique et immutable…)
* Les nouvelles méthodes de stream (surtout takeWhile et dropWhile, mais les autres aussi)
* ifPresentOrElse de l’Optional
* InputStream.readAllBytes[], que je ne connaissais pas, tellement pratique !
J’ai pu tetser un peu Jshell, je n’étais pas convaincu, je trouve que ce n’était pas le même feeling qu’en Scala ou en Clojure. Par contre, ça peut être pratique pour tester un truc du langage dont on est pas certain.
J’ai hâte de voir venir le pattern matching et les collections immutables quand même. Après, il ne manquera plus que le syntaxic sugar pour initialiser les maps et ça sera parfait !
Petit tips pour finir :
Tu peux transformer ‘Arrays.asList(items).stream()’ en ‘Arrays.stream(items)’ si items est un tableau. Ca évite le passage par une collection supplémentaire.
Alex