Highlights of the latest upgrade of standard Java include primitive classes, sealed classes, records, a vector API, and ports for Windows on ARM64 and Alpine Linux.
@_tamanm
|
|
|
Mohamed Taman
Chief Solutions Architect
CEO @SiriusXI | Java Champion | Oracle ACE | Jakarta EE Ambassador Author | trainer | speaker.
Or by googling 🕵 me
"Mohamed Taman"
@_tamanm
|
|
➥
➥
➥
➥
➥
@_tamanm
|
|
JDK 16 is a feature release backed by just six months of support from Oracle.
JDK 17, due in September, will be a long-term support (LTS) release.
Oracle frames JDK16 as a starting point for migration to JDK 17, with users able to test on JDK 16 before deploying to JDK17.
- LTS releases are published every two years.
Check out Java's important features!
➥
@_tamanm
|
|
@_tamanm
|
|
Preview Feature.
Incubator Modules.
Deprecation.
Features Marked for Removals.
@_tamanm
|
|
@_tamanm
|
|
Since JDK 12
whose design, specification, and implementation are complete, but which would benefit from a period of broad exposure and evaluation before either achieving final and permanent status in the Java SE Platform or else being refined or removed.
@_tamanm
|
|
mohamed_taman:~$ jshell --enable-preview
mohamed_taman -- -bash
When using Jshell:
When using maven:
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>${maven-compiler-plugin.version}</version>
<configuration>
<release>${java.version}</release>
<compilerArgs>
<arg>--enable-preview</arg>
</compilerArgs>
...
</configuration>
</plugin>
mohamed_taman -- -bash
When using IDE:
@_tamanm
|
|
Since JDK 9
For example, the HTTP & WebSocket client module is introduced in Java 9 under incubator module jdk.incubator.httpclient, and in Java 11, it goes to production under module java.net.http.
@_tamanm
|
|
Since JDK 9
Check out Java's important features!
✓
➥
@_tamanm
|
|
@_tamanm
|
|
@_tamanm
|
|
1st Prev JDK 14
Final JDK 16
2nd Prev JDK 15
class Point {
private final int x;
private final int y;
Point(int x, int y) {
this.x = x;
this.y = y;
}
int x() { return x; }
int y() { return y; }
public boolean equals(Object o) {
if (!(o instanceof Point)) return false;
Point other = (Point) o;
return other.x == x && other.y == y;
}
public int hashCode() {
return Objects.hash(x, y);
}
public String toString() {
return String.format("Point[x=%d, y=%d]", x, y);
}
}
@_tamanm
|
|
record Point(int x, int y) { }
By reducing the ceremony of Java by cutting boilerplate code. So the Point class could be re-written as:
Constructors for record classes
record Point(int x, int y){
Point(int x, int y){
if(x == 0 && y == 0)
throw new IllegalArgumentException(String.format("(%d,%d)", x, y));
this.x = x;
this.y = y;
}
}
record Point(int x, int y){
Point{
if(x == 0 && y == 0)
throw new IllegalArgumentException (String.format("(%d,%d)", x, y));
}
}
List<Merchant> findTopMerchants(List<Merchant> merchants, int month) {
// Local record
record MerchantSales(Merchant merchant, double sales) {}
return merchants.stream()
.map(merchant -> new MerchantSales(merchant, computeSales(merchant, month)))
.sorted((m1, m2) -> Double.compare(m2.sales(), m1.sales()))
.map(MerchantSales::merchant)
.collect(toList());
}
Local Record Classes
Continue
@_tamanm
|
|
1st Prev JDK 14
Final JDK 16
2nd Prev JDK 15
public boolean equals(Object o) {
return (o instanceof CaseInsensitiveString) &&
((CaseInsensitiveString) o).s.equalsIgnoreCase(s);
}
public boolean equals(Object o) {
return (o instanceof CaseInsensitiveString cis) &&
cis.s.equalsIgnoreCase(s);
}
public boolean equals(Object o) {
if (!(o instanceof Point))
return false;
Point other = (Point) o;
return x == other.x
&& y == other.y;
}
public boolean equals(Object o) {
return (o instanceof Point other)
&& x == other.x
&& y == other.y;
}
@_tamanm
|
|
1st Prev JDK 17
static String formatter(Object o) {
String formatted = "unknown";
if (o instanceof Integer i) {
formatted = String.format("int %d", i);
} else if (o instanceof Long l) {
formatted = String.format("long %d", l);
} else if (o instanceof Double d) {
formatted = String.format("double %f", d);
} else if (o instanceof String s) {
formatted = String.format("String %s", s);
}
return formatted;
}
static String formatterPatternSwitch(Object o) {
return switch (o) {
case Integer i -> String.format("int %d", i);
case Long l -> String.format("long %d", l);
case Double d -> String.format("double %f", d);
case String s -> String.format("String %s", s);
default -> o.toString();
};
}
@_tamanm
|
|
Continue
We may wish to handle null in the same way as another case label. For example:
static void testFooBar(String s) {
if (s == null) {
System.out.println("oops!");
return;
}
switch (s) {
case "Foo", "Bar" -> System.out.println("Great");
default -> System.out.println("Ok");
}
}
static void testFooBar(String s) {
switch (s) {
case null -> System.out.println("Oops");
case "Foo", "Bar" -> System.out.println("Great");
default -> System.out.println("Ok");
}
}
static void testStringOrNull(Object o) {
switch (o) {
case null, String s -> System.out.println("String: " + s);
}
}
sealed interface S permits A, B, C {}
final class A implements S {}
final class B implements S {}
record C(int i) implements S {} // Implicitly final
static int testSealedCoverage(S s) {
return switch (s) {
case A a -> 1;
case B b -> 2;
case C c -> 3;
};
}
@_tamanm
|
|
1st Prev JDK 15
Final JDK 17
2nd Prev JDK 16
package com.example.geometry;
public abstract sealed class Shape
permits Circle, Rectangle, Square, WeirdShape { ... }
public final class Circle extends Shape { ... }
public sealed class Rectangle extends Shape
permits TransparentRectangle, FilledRectangle { ... }
public final class TransparentRectangle extends Rectangle { ... }
public final class FilledRectangle extends Rectangle { ... }
public final class Square extends Shape { ... }
public non-sealed class WeirdShape extends Shape { ... }
A class is sealed by applying the sealed modifier to its declaration. Then, after any extends and implements clauses, the permits clause specifies the classes permitted to extend the sealed class.
@_tamanm
|
|
1st Prev JDK 15
Final JDK 17
2nd Prev JDK 16
Here is another classic example of a class hierarchy with a known set of subclasses: modeling mathematical expressions.
Sealed classes work well with record classes. Record classes are implicitly final, so a sealed hierarchy of record classes is slightly more concise than the example above:
package com.example.expression;
public sealed interface Expr
permits ConstantExpr, PlusExpr, TimesExpr, NegExpr { ... }
public final class ConstantExpr implements Expr { ... }
public final class PlusExpr implements Expr { ... }
public final class TimesExpr implements Expr { ... }
public final class NegExpr implements Expr { ... }
package com.example.expression;
public sealed interface Expr
permits ConstantExpr, PlusExpr, TimesExpr, NegExpr { ... }
public record ConstantExpr(int i) implements Expr { ... }
public record PlusExpr(Expr a, Expr b) implements Expr { ... }
public record TimesExpr(Expr a, Expr b) implements Expr { ... }
public record NegExpr(Expr e) implements Expr { ... }
Sealing and record classes
The combination of sealed classes and record classes is sometimes referred to as algebraic data types: Record classes allow us to express product types, and sealed classes allow us to express sum types.
@_tamanm
|
|
JDK-8250629
Final JDK 16
Before JDK 16, the javac compiler accepted C-style array declarations in record components. In particular, the compiler had accepted code such as:
This code is no longer acceptable according to the JLS specification for JDK 16:
record R(int i[]) {}
jshell> record R(int i[]) {}
| Error:
| legacy array notation not allowed on record components
| record R(int i[]) {}
| ^
mohamed_taman -- -bash
@_tamanm
|
|
JDK-8250741
Final JDK 16
Before JDK 16, the javac compiler accepted annotations declared as local interfaces. For example, the javac compiler had accepted code such as:
This code is no longer acceptable according to Section [14.3] of the JLS 16: "A local interface may be a normal interface (§9.1), but not an annotation interface (§9.6)."
class C {
void m() {
@interface A {}
}
}
jshell> class C {
...> void m() {
...> @interface A {}
...> }
...> }
| Error:
| annotation type declaration not allowed here
| @interface A {}
| ^-------------^
mohamed_taman -- -bash
Check out Java's important features!
✓
➥
✓
@_tamanm
|
|
@_tamanm
|
|
@_tamanm
|
|
JDK-8247781
Final JDK 16
[mtaman]:~ ~~ jshell --enable-preview
| Welcome to JShell -- Version 17-ea
| For an introduction type: /help intro
jshell> import java.time.format.DateTimeFormatter
jshell> import java.time.LocalTime
jshell> DateTimeFormatter.ofPattern("B").format(LocalTime.now())
$3 ==> "at night"
mohamed_taman -- -bash
The following example translating the day periods, and produces day period text depending on the time of the day and locale:
@_tamanm
|
|
JDK-8180352
Final JDK 16
Since its introduction in Java 8, the Stream API was blamed for its verboseness. For example, performing a simple mapping transformation on a list requires writing as much as:
However, this is not just a simple shortcut. Why?
list.stream().map(fn).collect(Collectors.toList())
list.stream().map(fn).toList()
@_tamanm
|
|
Final JDK 16
jshell> Integer x = new Integer("12")
| Warning:
| Integer(java.lang.String) in java.lang.Integer has been deprecated and marked for removal
| Integer x = new Integer("12");
| ^---------------^
x ==> 12
jshell> Integer x = new Integer(12)
| Warning:
| Integer(int) in java.lang.Integer has been deprecated and marked for removal
| Integer x = new Integer(12);
| ^-------------^
x ==> 12
jshell> Integer x = 12
x ==> 12
mohamed_taman -- -bash
When running on a future Java release in which the migration has occurred:
@_tamanm
|
|
Final JDK 16
To support Unix-domain socket channels, the following API elements have been added:
@_tamanm
|
|
Final JDK 16
@_tamanm
|
|
Final JDK 16
@_tamanm
|
|
JDK 16
@_tamanm
|
|
JDK 16
1st Inc. JDK 14
2nd inc. JDK 15
@_tamanm
|
|
JDK 17
1st Inc. JDK 16
@_tamanm
|
|
Final JDK 16
Check out Java's important features!
✓
➥
✓
✓
@_tamanm
|
|
@_tamanm
|
|
New things come ... and sometimes old things have to go ...
@_tamanm
|
|
Final JDK 17
@_tamanm
|
|
Final JDK 17
@_tamanm
|
|
Final JDK 17
@_tamanm
|
|
Final JDK 16
@_tamanm
|
|
Obsoleted Option |
Unified Logging |
-XX:+TraceClassLoading | -Xlog:class+load=info |
-XX:+TraceClassUnloading | -Xlog:class+unload=info |
-XX:+TraceExceptions | -Xlog:exceptions=info |
Final JDK 16
Check out Java's important features!
✓
➥
✓
✓
✓
@_tamanm
|
|
@_tamanm
|
|
@_tamanm
|
|
Final JDK 16
Supports native packaging formats to give end-users a natural installation experience. These formats include:
- msi and exe on Windows,
- pkg, and dmg on macOS, and
- deb and rpm on Linux.
Suppose you have an application composed of JAR files, all in a directory named lib, and that lib/main.jar contains the main class. Then the command:
[mtaman]:~ ~~ jpackage --name myapp --input lib --main-jar main.jar
mohamed_taman -- -bash
--main-class myapp.Main
--type pkg
@_tamanm
|
|
Final JDK 16
@_tamanm
|
|
Final JDK 16
@_tamanm
|
|
Final JDK 17
Check out Java's important features!
✓
✓
✓
✓
@_tamanm
|
|
✓
@_tamanm
|
|