Friday, June 24, 2022

Java_10_Features - ALL

 Table of Contents 

  • 10.1 Local-Variable Type Inference
  • 10.2 Immutable/Unmodifiable Collections
  • 10.3 Optional.orElseThrow()
  • 10.4 Time-Based Release Versioning
  • 10.5 Performance Improvement
  • 10.6 Other Changes in Java 10
  • 10.1 Local-Variable Type Inference

    Since Java 10, we can use the keyword var to declare local variables (local means: within methods). This allows, for example, the following definitions:

    var i = 10; var hello = "Hello world!"; var list = List.of(1, 2, 3, 4, 5); var httpClient = HttpClient.newBuilder().build(); var status = getStatus();
    Code language: Java (java)

    For comparison – this is how the definitions look in classic notation:

    int i = 10; String hello = "Hello world!"; List<Integer> list = List.of(1, 2, 3, 4, 5); HttpClient httpClient = HttpClient.newBuilder().build(); Status status = getStatus();
    Code language: Java (java)

    To what extent you use var will probably lead to lengthy discussions in many teams. I use it if it is a) significantly shorter and b) I can clearly see the data type in the code.

    In the example above, this would be the case in lines 3 and 4 (for List and HttpClient). The classic notation is much longer in both cases. And the assignments on the right – i.e. List.of() and HttpClient.newBuilder().build() – let me clearly see the data type.

    In the following cases, on the other hand, I would refrain from using var:

    • In line 1, you don't save a single character; here, I would stick with int.
    • In line 2, var is only minimally shorter than String – so I would rather use String here, too. But I also understand if teams decide otherwise.
    • In line 5, I would stick with the old notation. Otherwise, I can't tell offhand what getStatus() returns. Is it an int? A String? An enum? A complex value object? Or even a JPA entity from the database?

    10.2 Immutable/Unmodifiable Collections

    Methods Collections.unmodifiableList()unmodifiableSet()unmodifiableMap()unmodifiableCollection() – and four further variants for sorted and navigable sets and maps – the Java Collections Framework offers the possibility to create unmodifiable wrappers for collection classes.

    Here is an example:

    List<Integer> list = new ArrayList<>(); list.add(1); list.add(2); list.add(3); List<Integer> unmodifiable = Collections.unmodifiableList(list);
    Code language: Java (java)

    If we now try to add an element via the wrapper, we get an UnsupportedOperationException:

    unmodifiable.add(4); ⟶ Exception in thread "main" java.lang.UnsupportedOperationException at java.base/java.util.Collections$UnmodifiableCollection.add(...) at ...
    Code language: Java (java)

    However, the wrapper does not prevent us from modifying the underlying list. All subsequent changes to it are also visible in the wrapper. 

    This is because the wrapper does not contain a copy of the list, but a view:

    list.add(4); System.out.println("unmodifiable = " + unmodifiable); ⟶ unmodifiable = [1, 2, 3, 4]
    Code language: Java (java)

    List.copyOf(), Set.copyOf(), and Map.copyOf()

    With Java 10, we now also have the possibility to create immutable copies of collections. For this purpose, we have the static interface methods List.copyOf()Set.copyOf() and Map.copyOf().

    If we create such a copy and then modify the original collection, the changes will no longer affect the copy:

    List<Integer> immutable = List.copyOf(list); list.add(4); System.out.println("immutable = " + immutable); ⟶ immutable = [1, 2, 3]
    Code language: Java (java)

    The attempt to change the copy is – just like when using unmodifiableList() – acknowledged with an UnsupportedOperationException:

    immutable.add(4); ⟶ Exception in thread "main" java.lang.UnsupportedOperationException at java.base/java.util.ImmutableCollections.uoe(...) at java.base/java.util.ImmutableCollections$AbstractImmutableCollection.add(...) at ...
    Code language: Java (java)

    Note: Should you need a modifiable copy of the list, you can always use the copy constructor:

    List<Integer> copy = new ArrayList<>(list);
    Code language: Java (java)

    Collectors.toUnmodifiableList(), toUnmodifiableSet(), and toUnmodifiableMap()

    The collectors created using Collectors.toList()toSet() and toMap() collect the elements of a Stream into mutable lists, sets and maps. 

    The following example shows the use of these collectors and the subsequent modification of the results:

    List<Integer> list = IntStream.rangeClosed(1, 3).boxed().collect(Collectors.toList()); Set<Integer> set = IntStream.rangeClosed(1, 3).boxed().collect(Collectors.toSet()); Map<Integer, String> map = IntStream.rangeClosed(1, 3).boxed() .collect(Collectors.toMap(Function.identity(), String::valueOf)); list.add(4); set.add(4); map.put(4, "4"); System.out.println("list = " + list); System.out.println("set = " + set); System.out.println("map = " + map);
    Code language: Java (java)

    As you would expect, the program produces the following output (although the elements of the set and map may appear in a different order):

    list = [1, 2, 3, 4] set = [1, 2, 3, 4] map = {1=1, 2=2, 3=3, 4=4}
    Code language: plaintext (plaintext)

    Methods Collectors.toUnmodifiableList()toUnmodifiableSet(), and toUnmodifiableMap() have been added, which now allow us to collect stream elements into immutable lists, sets, and maps:

    List<Integer> list = IntStream.rangeClosed(1, 3).boxed().collect(Collectors.toUnmodifiableList()); Set<Integer> set = IntStream.rangeClosed(1, 3).boxed().collect(Collectors.toUnmodifiableSet()); Map<Integer, String> map = IntStream.rangeClosed(1, 3) .boxed() .collect(Collectors.toUnmodifiableMap(Function.identity(), String::valueOf));
    Code language: Java (java)


    10.3 Optional.orElseThrow()

    Optional, introduced in Java 8, provides the get() method to retrieve the value wrapped by the Optional. Before calling get(), you should always check with isPresent() whether a value exists:

    Optional<String> result = getResult(); if (result.isPresent()) { System.out.println(result.get()); }
    Code language: Java (java)

    If the Optional is empty, get() would otherwise throw a NoSuchElementException.

    To minimize the risk of an unintended exception, IDEs and static code analysis tools issue a warning if get() is used without isPresent():

    However, there are also cases where such an exception is desired. Previously, one had to add appropriate @SuppressWarnings annotations to the code to suppress the warnings.

    Java 10 offers a nicer solution with the method orElseThrow(): The method is an exact copy of the get() method – only the name is different. Since it is clear from the name that this method can throw an exception, misunderstandings are ruled out. The static code analysis no longer criticizes the usage as a code smell.

    Here is the source code of both methods for comparison:

    public T get() { if (value == null) { throw new NoSuchElementException("No value present"); } return value; } public T orElseThrow() { if (value == null) { throw new NoSuchElementException("No value present"); } return value; }

    10.4 Time-Based Release Versioning

    java.util.Optionaljava.util.OptionalDoublejava.util.OptionalIntand java.util.OptionalLongeach got a new method orElseThrow()which doesn't take any argument and throws NoSuchElementExceptionif no value is present:

    @Test public void whenListContainsInteger_OrElseThrowReturnsInteger() { Integer firstEven = someIntList.stream() .filter(i -> i % 2 == 0) .findFirst() .orElseThrow(); is(firstEven).equals(Integer.valueOf(2)); }

    It's synonymous with and is now the preferred alternative to the existing get()method.


    10.5 Performance Improvement

    10.5.1 Parallel Full GC for G1
    The G1 garbage collector is the default one since JDK 9. However, the full GC for G1 used a single threaded mark-sweep-compact algorithm.

    This has been changed to the parallel mark-sweep-compact algorithm in Java 10 effectively reducing the stop-the-world time during full GC.

    10.5.2 Application Class-Data SharinClass-Data Sharing

    introduced in JDK 5, allows a set of classes to be pre-processed into a shared archive file that can then be memory-mapped at runtime to reduce startup time which can also reduce dynamic memory footprint when multiple JVMs share the same archive file.

    CDS only allowed the bootstrap class loader, limiting the feature to system classes only. Application CDS (AppCDS) extends CDS to allow the built-in system class loader (a.k.a., the “app class loader”), the built-in platform class loader, and custom class loaders to load archived classes. This makes it possible to use the feature for application classes.

    We can use the following steps to make use of this feature:

    1. Get the list of classes to archive

    The following command will dump the classes loaded by the HelloWorld application into hello.lst:

    $ java -Xshare:off -XX:+UseAppCDS -XX:DumpLoadedClassList=hello.lst \ 
        -cp hello.jar HelloWorld

    2. Create the AppCDS archive

    Following command creates hello.js a using the hello.lst as input:

    $ java -Xshare:dump -XX:+UseAppCDS -XX:SharedClassListFile=hello.lst \
        -XX:SharedArchiveFile=hello.jsa -cp hello.jar

    3. Use the AppCDS archive

    Following command starts the HelloWorld application with hello.jsa as input:

    $ java -Xshare:on -XX:+UseAppCDS -XX:SharedArchiveFile=hello.jsa \
        -cp hello.jar HelloWorld

    AppCDS was a commercial feature in Oracle JDK for JDK 8 and JDK 9. Now it is open sourced and made publicly available.

    10.5.4. Experimental Java-Based JIT Compiler

    Graal is a dynamic compiler written in Java that integrates with the HotSpot JVM; it's focused on high performance and extensibility. It's also the basis of the experimental Ahead-of-Time (AOT) compiler introduced in JDK 9.

    JDK 10 enables the Graal compiler, to be used as an experimental JIT compiler on the Linux/x64 platform.

    To enable Graal as the JIT compiler, use the following options on the java command line:

    -XX:+UnlockExperimentalVMOptions -XX:+UseJVMCICompiler

    Note that this is an experimental feature and we may not necessarily get better performance than the existing JIT compilers.


    10.6 Other Changes in Java 10

    10.6.1 Heap Allocation on Alternative Memory Devices

    With the implementation of JEP 316, you can now allocate the Java heap – instead of on conventional RAM – on an alternative memory device such as NV-DIMM (non-volatile memory).

    The alternative memory must be provided by the operating system via a file system path (e.g., /dev/pmem0) and is included via the following option on the java command line

    -XX:AllocateHeapAt=<path>

    Additional Unicode Language-Tag Extensions

    JDK Enhancement Proposal 314 adds so-called "language-tag extensions". These allow to store the following additional information in a Locale object:

    Key Description Examples
    cu Currency ISO 4217 currency codes
    fw First day of week sun (Sunday), mon (Monday)
    rg Region override uszzzz (US units)
    tz Timezone uslax (Los Angeles), deber (Berlin)

    The following two extensions have already existed since Java 7:

    Key Description Examples
    ca Calendar gregorian, buddhist, chinese
    nu Numbering system arab, roman

    The following example source code shows the creation of a German locale ("de-DE") with US dollar as currency ("cu-usd"), Wednesday as the first day of the week ("fw-wed"), and the Los Angeles time zone ("tz-uslax"):

    Locale locale = Locale.forLanguageTag("de-DE-u-cu-usd-fw-wed-tz-uslax"); Currency currency = Currency.getInstance(locale); Calendar calendar = Calendar.getInstance(locale); DayOfWeek firstDayOfWeek = DayOfWeek.of((calendar.getFirstDayOfWeek() + 5) % 7 + 1); DateFormat dateFormat = DateFormat.getTimeInstance(LONG, locale); String time = dateFormat.format(new Date()); System.out.println("currency = " + currency); System.out.println("firstDayOfWeek = " + firstDayOfWeek); System.out.println("time = " + time);
    Code language: Java (java)

    At the time of writing this article (8:45 p.m. in Berlin), the program prints the following:

    currency = USD firstDayOfWeek = WEDNESDAY time = 11:45:50 PDT
    Code language: plaintext (plaintext)

    In Java 9, the additional tags are ignored, and the program prints the following (40 seconds later):

    currency = EUR firstDayOfWeek = MONDAY time = 20:46:30 MESZ
    Code language: plaintext (plaintext)

    Since probably only very few Java developers have to deal with such details, I have placed this extension under "Other Changes".

    10.6.2 Garbage Collector Interface

    Until Java 9, some parts of the garbage collector source code were hidden within long if-else chains deep in the sources of the Java interpreter and the C1 and C2 compilers. To implement a new garbage collector, developers had to know all these places and extend them for their specific needs.

    JDK Enhancement Proposal 304 introduces a clean garbage collector interface in the JDK source code, isolating the garbage collector algorithms from the interpreter and compilers.

    The interface will allow developers to add new GCs without having to adjust the code base of the interpreter and compiler.

    10.6.3 Root Certificates

    Until Java 9, the OpenJDK did not include root certificates in the cacerts keystore file, so SSL/TLS-based features were not readily executable.

    With JDK Enhancement Proposal 319, the root certificates contained in the Oracle JDK were adopted in the OpenJDK.

    10.6.4 Thread-Local Handshakes

    Thread-local handshakes are an optimization to improve VM performance on x64 and SPARC-based architectures. The optimization is enabled by default. You can find details in JDK Enhancement Proposal 312.

    10.6.5 Remove the Native-Header Generation Tool

    With JEP 313, the javah tool was removed, which developers could use to generate native header files for JNI. The functionality has been integrated into the Java compiler, javac.

    10.6.6 Consolidate the JDK Forest into a Single Repository

    In JDK 9, the source code was located in eight separate Mercurial repositories, which often led to considerable additional work during development. For over a thousand changes, it was necessary to distribute logically related commits across multiple repositories.

    With JEP 296, the entire JDK source code was consolidated into a monorepo. The monorepo now allows atomic commits, branches, and pull requests, making development on the JDK much easier.

    Complete List of All Changes in Java 10

    This article has presented all the features of Java 10 that are defined in JDK Enhancement Proposals, as well as enhancements to the JDK class library that are not associated with any JEP.


    You may also like

    Kubernetes Microservices
    Python AI/ML
    Spring Framework Spring Boot
    Core Java Java Coding Question
    Maven AWS