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:
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:
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:
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) {
thrownew NoSuchElementException("No value present");
}
return value;
}
public T orElseThrow(){
if (value == null) {
thrownew NoSuchElementException("No value present");
}
return value;
}
10.4 Time-Based Release Versioning
java.util.Optional, java.util.OptionalDouble, java.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.
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:
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:
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"):
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.