Source:
The following notes are extracted from a Pluralsight Course: What’s New in Java 9 which gives a good overview of the interesting features of Java 9.
1. Java Platform Module System
1.1. Key Concepts
- Module provides another layer of encapsulation, interface definition, dependency management within a single jar file or a bunch or jars within a classpath
- Even the core java (rt.jar) itself is modularized now (e.g. it by default only has java.base, it doesn’t have java.xml).
- JavaDoc now shows which module a class belongs to.
- You might encounter issues if you use non-default modules (e.g. ones ship as part java.se.ee). Solution: add
--add-modules
flag when compiling & running
1.2. Sample module descriptors
module-info.java
in projecttwo
module two {
exports com.example.person; //defines what to export
}
module-info.java
in projectone
module one {
requires java.logging; // from standard library
requires two; // an existing predefined module
}
NOTE:
- You’ll only be able to access
public
lyexport
ed classes in the defined packages. -
In IntelliJ IDEA, you need to explicitly define the module dependencies in the
Project Settings
-Modules
on top of Java 9’s module definitions. The two module systems are independent of each other. Refer to the paragraph towards the end in this jetbrains blogpost. E.g.
1.3. Useful commands
# list all JDK modules
java --list-modules
# display the module info
java --describe-module java.sql
# using non-default modules
# compile
javac --add-modules java.xml.bind Main.java
# run
java --add-module java.xml.bind Main
# jdeps can be used to see all package level dependencies:
jdeps -jdkinternals Main.class
jdeps --module-path outDir --module main.app
NOTE: At runtime, Java 9 is backward compatible. That means, even if you use private encapsulated types (e.g. X500Name) in your jdk 8 codes, that jar will still run in Java 9, but with a warning. If you want to deny such access, you can use --illegal-access=deny
flag which will be the default in future release.
However, at compile time, uses of private encapsulated types will throw an error. You can patch by using the new javac
’s --add-exports
flag.
1.4. Transitivity
Dependency of module is not transitive by default. You need requires transitive
for transitive dependencies. E.g.:
module java.sql {
// ...
requires transitive java.logging
requires transitive java.xml
}
Users of java.sql
will automatically get java.logging
and java.xml
.
You can also use requires transitive
to create an aggregator module.
1.5. Qualified exports
You can do qualified exports to expose some package to a limited package.
E.g. when you use javafx, LauncherImpl
uses reflection to access your defined class. To fix, you can add the below:
exports javamodularity.easytext.gui to javafx.graphics
1.6. Services
- Supports the notion of “depending on interfaces instead of implementations”.
- Similar to Service Discovery & Registration concept in microservices, Dependency Injection in Spring and the old java JNDI.
API Interface definition:
module myApi {
exports com.api;
}
interface MyService {
void doWork();
}
Consumer of the API:
module myConsumer {
requires myApi;
uses com.api.MyService;
}
Implementor/ Provider of the API interface:
module myProvider {
requires myApi;
provides com.api.MyService
with myProvider.MyServiceImpl;
}
Using ServiceLoader
to retrieve all available implementations:
ServiceLoader<MyService> services = ServiceLoader.load(MyService.class);
for (MyService svc: services) {
svc.doWork();
}
1.7. jlink
- Linking creates an optimized custom runtime image with low footprint (e.g. don’t need the entire rt.jar).
# create the image
jlink --module-path <modulepath> --add-modules <modules> --limit-modules <modules> --output <path>
# then run the image
image/bin/easytext.cli test.txt
2. Jshell
- Auto adds semicolon (“
;
”) - all commands starts with slash (“
/
”). E.g.
/exit
/help
/vars // show all declared vars
/imports // show all declared imports
/methods // show all declared methods
/types // show all declared types
/save mysession.jsh // save session
/open mysession.jsh // open existing session
/open Person.java // import java file
- If you want to add a library with jshell, use the below:
jshell --class-path commons-lang-3-3-5.jar
3. Collection Factory Methods
- The generated collection is immutable.
- Sample:
List<Integer> ints = List.of(1,2,3);
Set.of("first", "second");
// cannot put duplicate
// cannot put null
Map.of("Key1",1,"Key2",2);
// alternate key and value
Map.ofEntries(Map.entry("Key1", true), Map.entry("Key2",false));
// cannot put duplicate key
4. New Stream
Methods
takeWhile
: collect the item in the stream while the predicate is true.
Stream.of("a", "b", "c", "de", "f", "g", "h")
.takeWhile(s -> s.length() <= 1)
.collect(Collectors.toList()); // [a, b, c]
dropWhile
: skip the item the stream while the predicate is true.
Stream.of("a", "b", "c", "de", "f", "g", "h")
.dropWhile(s -> s.length() <= 1)
.collect(Collectors.toList()); // [de, f, g, h]
ofNullable
: conveniently transform possibly null value to Stream. Handy while working with old APIs which can return null.
long one = Stream.ofNullable("42").count(); // 1
long zero = Stream.ofNullable(null).count(); // 0
iterate
: now overloaded with a way to create a finite stream
// Java 8
Stream.iterate(0, i -> i + 1)
.forEach(System.out::println); // 1 2 3 4 5
// Java 9
Stream.iterate(0, i -> i < 5, i -> i + 1)
.forEach(System.out::println); // 1 2 3 4
5. New Optional
Methods
Optional.ifPresentOrElse
: nothing fancy, works as named
// before
if (a.isPresent()) {
System.out.println(a.get());
} else {
System.out.println("Nothing");
}
// now
a.ifPresentOrElse(System.out::println, () -> System.out.println("Nothing"));
Optional.stream
: helps improve interoperability betweenStream
s andOptional
s. Use it withflatMap
.
Stream<Optional<Integer>> optionals = Stream.of(Optional.of(1), Optional.empty(), Optional.of(2));
Stream<Integer> ints = optionals.flatMap(Optional::stream);
ints.forEach(System.out::println); // 1 2
Optional.or
: Alternative way to defaulting empty case without unboxing theOptional
container.
public static void main(String... args) {
Optional<Book> localFallback = Optional.of(Book.getBook());
// Before Optional.or
Book bestBookBefore = getBestOffer()
.orElse(getExternalOffer().orElse(localFallback.get())); // .get() is BAD!
Optional<Book> bestBook =
getBestOffer()
.or(() -> getExternalOffer())
.or(() -> localFallback);
System.out.println(bestBook);
}
static Optional<Book> getBestOffer() {
return Optional.empty();
}
static Optional<Book> getExternalOffer() {
return Optional.of(new Book("External Book", Set.of(), 11.99));
}
6. Private Method in Interfaces
public interface PricedObject {
// Private fields are not allowed
// private double TAX = 1.21;
double getPrice();
/* Before private interface methods, shared logic could not be extracted into a
new method (at least not without it becoming part of the public API).
[getPrice() * 1.21] is duplicated below.
default double getPriceWithTax() {
return getPrice() * 1.21;
}
default double getOfferPrice(double discount) {
return getPrice() * 1.21 * discount;
}
*/
default double getPriceWithTax() {
return getTaxedPriceInternal();
}
default double getOfferPrice(double discount) {
return getTaxedPriceInternal() * discount;
}
// This is now possible in Java 9
private double getTaxedPriceInternal() {
return getPrice() * getTax();
}
private static double getTax() {
return 1.21;
}
}
7. Process APIs
// get current process's PID
long pid = ProcessHandle.current().pid();
// get the process by PID
Optional<ProcessHandle> ph = ProcessHandle.of(123); // 123 is the PID
// get parent
Optiional<ProcessHandle> parentsPh = ph.flatMap(p -> p.parent());
// get info
ph.map(ProcessHandle::info).ifPresent(System.out::println);
// [
// user: Optional[****],
// cmd: /Library/Java/JavaVirtualMachines/jdk-9.0.1.jdk/Contents/Home/bin/jshell,
// startTime: Optional[2017-11-21T15:02:31.672Z]
// ]
// kill process
ph.ifPresent(p -> p.destroy());