Java 10
Local Type Inference
public void printName(String firstName, String lastName) {
var fullName = String.format("%s %s", firstName, lastName);
System.out.println(fullname);
}
Java 11 (LTS)
Local Type Inference for Lamda
(var firstName, var lastName) -> firstName + lastName;
Java 14
Switch Expression w/ Return or Yield
Switch expressions can now return values.
switch (day) {
case MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY: -> return "weekday";
case SATURDAY, SUNDAY: -> return "weekend";
default: -> throw new Exception("Not a valid day");
}
Yield keyword is used when a code block is defined.
String dateCategory = switch (day) {
case MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY: -> "weekday";
case SATURDAY, SUNDAY: -> "weekend";
default: -> {
int len = day.length();
yield len % 2 == 0 ? "weekday" : "weekend";
}
}
Helpful NullPointerException Message
Exception in thread "main" java.lang.NullPointerException:
Cannot assign field "age" because "author" is null
Java 15
Text Blocks
String text = """
This is my long text block.
""";
Java 16
Pattern Match for instanceof
// before
if (obj instanceof String) {
String s = (String) obj;
...
}
// now
if (obj instanceof String s) {
...
}
Records
Records simplify the creation of immutable POJOs. They act almost like classes but you can’t subclass them.
record Key(String id, String name) {}
Java 17 (LTS)
Sealed
The sealed keyword on a class decleration allows you to restrict which classes can subclass the class being declared.
public abstract sealed class Human
permits Male, Female { ... }
Java 21 (LTS)
Pattern Matching on Instance Type
String formatted = switch (o) {
case Integer i when i > 10 -> String.format("a large Integer %d", i);
case Integer i -> String.format("a small Integer %d", i);
case Long l -> String.format("a Long %d", l);
default -> o.toString();
};
Record Pattern Matching w/ Deconstruction
if (r instanceof ColoredPoint(Point2D(int x, int y), Color c)) {
// work with x, y, and c
}
Virtual Threads
Virtual threads solve the problem of blocking I/O operation making threads idle. Blocking operations will now release the thread so that the thread can work on something else while the operations remains blocking. Once the operation is unblocked, it rejoins the queue of waiting tasks to be worked on.
// With OS threads, this would be very problematic
try (var executor = Executors.newVirtualThreadPerTaskExecutor()) {
IntStream.range(0, 10_000).forEach(i -> {
executor.submit(() -> {
Thread.sleep(Duration.ofSeconds(1));
System.out.println("" + i);
return i;
});
});
}