Mohamed Taman

Chief Solutions Architect

Java Champion | CEO @SiriusXI | Oracle ACE Alumni | Jakarta EE Ambassador | Author | Trainer | Speaker.

Java 11-21 Evolution

Is Java still relevant and worth learning in 2024? 

We will look at Java’s evolution and impact on software development to show why it matters more than ever.

Java is more than a language.

@_tamanm

|

|

|

Publications

@_tamanm

|

|

|

You can catch me (🤝)

Here 👇

Or by googling 🕵 me

"Mohamed Taman"

@_tamanm

|

|

|

1

Java History.

2

What is an

LTS version?

3

Checkout  Java

11 - 17 opinionated features.

5

Resources

4

Checkout  Java

18 - 21 opinionated features.

Agenda

@_tamanm

|

|

|

1

Introduction

Java History  🥷

@_tamanm

|

|

|

@_tamanm

|

|

|

2

What is

an LTS version?

@_tamanm

|

|

|

@_tamanm

|

|

|

What is an LTS?

  • Long Term Support

  • Development Impact

  • For how long?

  • Who is supporting?

  • Marketing concept

  • Related to a specific vendor JDK

  • Changed from 3ys to every 2ys (now)

  • Usually 6-8 years

  • Bug fixes covered for long time

  • Performance improvement

  • New features

  • Vulnerabilities fixed

  • Commercial support

  • Each vendor decides how and when to ship

  • Azul has Medium Term Support (MTS)

3

Checkout

Java 11 - 17

opinionated features.

@_tamanm

|

|

|

Java 11 New Features (LTS)

@_tamanm

|

|

|

  • Language

  • JVM

  • API

  • Tools

  • Local Variable Syntax for Lambda Parameters (JEP 323)

  • Dynamic Class-File Constants (JEP 309)

  • Epsilon GC: A No-Op Garbage Collector (JEP 318)

  • ZGC: A Scalable Low-Latency GC - Experimental  (JEP 333)

  • Nest-Based Access Control (JEP 181)

  • Low-Overhead Heap Profiling (JEP 331)

  • Improve Aarch64 Intrinsics (JEP 315)

  • HTTP Client (JEP 321)
  • Unicode 10 Support (JEP 327)
  • Key Agreement with Curve25519 and Curve448 (JEP 324)
  • ChaCha20 and Poly1305 Cryptographic Algorithms (JEP 329)
  • Transport Layer Security TLS 1.3 (JEP 332)
  • Launch Single-File Source-Code Programs (JEP 330)
  • Flight Recorder (JEP 328)
  • Deprecation/Removals

  • Removed Java EE and CORBA Modules (JEP 320)
  • Deprecate Nashorn JavaScript Engine (JEP 335)
  • Deprecate the Pack200 Tools and API (JEP 336)

Java 11 is the first (LTS) release with the new release cadence.

@_tamanm

|

|

|

JEP 323: Local Variable Syntax for Lambda Parameters

(String a, String b) -> a.concat(b)
( a, b) -> a.concat(b)
(var a, var b) -> a.concat(b)
(@Nullable var a, @Nonnull var b) -> a.concat(b)

Because annotations needs a type

@_tamanm

|

|

|

JEP 330: Launch Single-File Source-Code Programs

public class HelloWorld {
  public static void main(String[] args) {
     System.out.println("Hello Java 🧡 Developers");
  }
}

Running .java old days:

  • Should be compiled to have .class

  • Then run .class with java

mohamed_taman$ javac HelloWorld.java

mohamed_taman$ java HelloWorld
Hello Java 🧡 Developers

And now RUN .java WITH Java like this:

mohamed_taman$ java HelloWorld.java
Hello Java 🧡 Developers

@_tamanm

|

|

|

JEP 330: Launch Single-File Source-Code Programs

#!/usr/bin/java --source 11

import java.nio.file.*;

public class DirectoryLister {
    public static void main(String[] args) throws Exception {

    var dirName = ".";

    if ( args == null || args.length < 1 ){
    System.err.println("Listing the current directory...");
    } else {
    dirName = args[0];
    }

    Files.walk(Paths.get(dirName)).forEach(out::println);
}}

SHELL SCRIPTING WITH JAVA - SHEBANG FILES:

mohamed_taman:code$ chmod +x dirlist

Run it as the following:

mohamed_taman:code$ ./dirlist
Listing the current directory...
.
./PalindromeChecker.java
./greater
./UsersHttpClient.java
./HelloWorld.java
./Greater.java
./dirlist

Save this code in a file named dirlist without any extension and then mark it as executable:

Continue

@_tamanm

|

|

|

JEP 321: HTTP Client

var client = HttpClient.newHttpClient();

var request = HttpRequest.newBuilder()
            .uri(create("https://postman-echo.com/get/"))
            .build();

client.sendAsync(request, ofString())
      .thenApply(HttpResponse::body)
      .thenAccept(out::println)
      .join();

HTTP Client Module:

public final class EchoListener implements WebSocket.Listener {
    @Override
    public void onOpen(WebSocket webSocket) {
        logger.info("CONNECTED");
        ....
    }
    ....
}

Use an HttpClient to create a WebSocket

WebSocket webSocket = httpClient.newWebSocketBuilder()
        .buildAsync(URI.create("ws://demos.kaazing.com/echo"), new EchoListener(executor)).join();

webSocket
  .sendClose(WebSocket.NORMAL_CLOSURE, "ok")
  .thenRun(() -> logger.info("Sent close"))
  .join();

HTTP Websocket Module:

Java 12 New Features

@_tamanm

|

|

|

  • Language

  • JVM

  • API

  • Switch Expressions - 1st Preview (JEP 325)

  • Shenandoah: A Low-Pause-Time Garbage Collector - Experimental  (JEP 189)
  • One AArch64 Port, Not Two (JEP 340)
  • Default CDS Archives (JEP 341)
  • G1GC : Abortable Mixed Collections for G1 (JEP 344)
  • G1GC: Promptly Return Unused Committed Memory from G1 (JEP 346)

The first Java release that included a preview feature (JEP 12). Such features are only available if the compiler and JVM is launched with the --enable-preview option.

@_tamanm

|

|

|

  • Language

  • JVM

  • Internal API

  • Switch Expressions - 2nd Preview (JEP 354)
  • Text Blocks - 1st Preview (JEP 355)
  • Dynamic CDS Archives (JEP 350)
  • ZGC: Uncommit Unused Memory (JEP 351)
  • Reimplement the Legacy Socket API (JEP 353)

Java 13 New Features

Java 14 New Features

@_tamanm

|

|

|

  • Language

  • JVM

  • API

  • Tools

  • Switch Expressions (JEP 361)
  • Records  - 1st Preview (JEP 359)
  • Text Blocks - 2nd Preview (JEP 368)
  • Pattern Matching for instanceof  - 1st Preview (JEP 305)
  • Non-Volatile Mapped Byte Buffers (JEP 352)
  • Foreign-Memory Access API - 1st Incubator (JEP 370)
  • Packaging Tool - 1st Incubator (JEP 343)
  • Deprecation/Removals

  • Deprecate the ParallelScavenge + SerialOld GC Combination (JEP 366)
  • Deprecate the Solaris and SPARC Ports (JEP 362)
  • Remove the Concurrent Mark Sweep (CMS) Garbage Collector (JEP 363)
  • Remove the Pack200 APIs & Tools (JEP 367)

@_tamanm

|

|

|

Here’s the old switch statement:

JEP 362: Switch Expressions

jshell> String getRomanNumber(int value){
    String romanValue = "";
    switch(value){
       case 0: romanValue = "nulla";
             break;
       case 1: romanValue = "I";
             break;
       case 2: romanValue = "II";
             break;
       case 3: romanValue = "III";
             break;
       case 4: romanValue = "IV";
             break;
       case 5: romanValue = "V";
             break;
       case 6: romanValue = "VI";
             break;
       case 7: romanValue = "VII";
             break;
       case 8: romanValue = "VIII";
             break;
       case 9: romanValue = "IX";
             break;
       case 10: romanValue = "X";
             break;
       default: System.out.printf("Out of range value: %d %n", value);
                romanValue = "N/A";
             break;
    }
   return romanValue;
}

@_tamanm

|

|

|

Here’s the new switch statement

Continue

JEP 362: Switch Expressions

String getRomanNumber(int value){
    String romanValue = "";
    switch(value){
       case 0 -> romanValue = "nulla";
       case 1 -> romanValue = "I";
       case 2 -> romanValue = "II";
       case 3 -> romanValue = "III";
       case 4 -> romanValue = "IV";
       case 5 -> romanValue = "V";
       case 6 -> romanValue = "VI";
       case 7 -> romanValue = "VII";
       case 8 -> romanValue = "VIII";
       case 9 -> romanValue = "IX";
       case 10 -> romanValue = "X";
       default -> {
              System.out.printf("Out of range value: %d %n", value);
              romanValue = "N/A";
        }
    }
    return romanValue;
}

New Switch expression

String getRomanNumber(int value){
    return switch(value){
       case 0 -> "nulla";
       case 1 -> "I";
       case 2 -> "II";
       case 3 -> "III";
       case 4 -> "IV";
       case 5 -> "V";
       case 6 -> "VI";
       case 7 -> "VII";
       case 8 -> "VIII";
       case 9 -> "IX";
       case 10 -> "X";
       default -> throw new IllegalStateException("Out of range value: " + value);
    };
}

@_tamanm

|

|

|

Multiple comma-separated labels in a single switch case

Continue

JEP 362: Switch Expressions

jshell> String getOddOrEvenNumber(int value){
    String kind = "N/A";
    switch(value){
       case 0: kind = "Zero"; break;
       case 1:
       case 3:
       case 5:
       case 7:
       case 9: kind = "Odd"; break;
       case 2:
       case 4:
       case 6:
       case 8:
       case 10: kind = "Even"; break;
       default: System.out.printf("Out of range: %d %n", value);
    }
   return kind;
}

The new way

jshell> var kind = switch(value){
  ...> case 0 -> "Zero";
  ...> case 1, 3, 5, 7, 9 -> "Odd";
  ...> case 2, 4, 6, 8, 10 -> "Even";
  ...> default -> throw new Exception("Out of range: " + value);
  ...> };
jshell> String kind = switch(value){
   ...>  case 0: yield "Zero";
   ...>  case 1, 3, 5, 7, 9: yield "Odd";
   ...>  case 2, 4, 6, 8, 10: yield "Even";
   ...>  default: throw new Exception("Out of range: " + value);
   ...>  };

@_tamanm

|

|

|

Before Java 14, the JVM will print out the method, filename, and line number that caused the NPE:

JEP 358: Helpful NullPointerExceptions

a.i = 99;

Exception in thread "main" java.lang.NullPointerException at Prog.main(Prog.java:5)

In Java 14 - Suppose an NPE occurs in this code:

a.b.c.i = 99;

Exception in thread "main" java.lang.NullPointerException:
    Cannot read field 'c' because 'a.b' is null.
    at Prog.main(Prog.java:5)
-XX:+ShowCodeDetailsInExceptionMessages

To enable this feature use the following JVM flag:

Which is on by default in Java 15.

Java 15 New Features

@_tamanm

|

|

|

  • Language

  • JVM

  • Internal API

  • API

  • Text Blocks (JEP 378)
  • Sealed Classes  - 1st Preview (JEP 360)
  • Pattern Matching for instanceof - 2nd Preview (JEP 375)
  • Records  - 2nd Preview (JEP 384)
  • ZGC: A Scalable Low-Latency Garbage Collector (JEP 377)
  • Shenandoah: A Low-Pause-Time Garbage Collector (JEP 379)
  • Reimplement the Legacy DatagramSocket API (JEP 373)
  • Edwards-Curve Digital Signature Algorithm (EdDSA) (JEP 339)
  • Hidden Classes (JEP 371)
  • Foreign-Memory Access API - 2nd Incubator (JEP 383)
  • Deprecation/Removals

  • Disable and Deprecate Biased Locking (JEP 374)
  • Deprecate RMI Activation for Removal (JEP 385)
  • Remove the Nashorn JavaScript Engine (JEP 372)
  • Remove the Solaris and SPARC Ports (JEP 381)

@_tamanm

|

|

|

Before Java 15, for multi-line text we construnct it like this:

JEP 378: Text Blocks

var html = "\n" +
           "<html>\n" +
           "    <body>\n" +
           "        <p>Hello, world</p>\n" +
           "    </body>\n" +
           "</html>\n";

In Java 15:

var html = """
            <html>
                <body>
                    <p>Hello, world</p>
                </body>
            </html>
            """;

Java 16 New Features

@_tamanm

|

|

|

  • Language

  • JVM

  • API

  • Tools

  • Pattern Matching for instanceof (JEP 394)
  • Records (JEP 395)
  • Warnings for Value-Based Classes (JEP 390)
  • Static Members in Inner Classes (JEP 395)
  • Sealed Classes - 2nd Preview (JEP 397)
  • ZGC: Concurrent Thread Processing (JEP 376)
  • Alpine Linux Port (JEP 386)
  • Windows/AArch64 Port (JEP 388)
  • Strongly Encapsulate JDK Internals by Default (JEP 396)
  • Elastic Metaspace (JEP 387)
  • Vector API - 1st Incubator (JEP 338)
  • Unix-Domain Socket Channels (JEP 380)
  • Foreign Linker API - 1st Incubator (JEP 389)
  • Foreign-Memory Access API - 3rd Incubator (JEP 393)
  • Internal

  • Enable C++14 Language Features (JEP 347)
  • Migrate from Mercurial to Git (JEP 357)
  • Migrate to Git/GitHub (JEP 369)

The OpenJDK source tree has moved from Mercurial to Git and is now hosted on GitHub.

@_tamanm

|

|

|

JEP 395: Records

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);
    }
}
  • It is a common complaint that "Java is too verbose" or has "too much ceremony." Some of the worst offenders are classes that are nothing more than immutable data carriers for a handful of values.
  • Properly writing such a data-carrier class involves a lot of low-value, repetitive, error-prone code: constructors, accessors, equals, hashCode, toString, etc.
  • For example, a class Point to carry x and y coordinates inevitably ends up like this:

@_tamanm

|

|

|

JEP 395: Records

Continue

record Point(int x, int y) { }
  • Providing records classes that act as transparent carriers for immutable data. Records can be considered nominal tuples.
  • By reducing the ceremony of Java by cutting boilerplate code. So the Point class could be re-written as:

Constructors for record classes

  • Canonical Constructor:
  • Compact Constructor:
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

@_tamanm

|

|

|

JEP 394: Pattern Matching for instanceof

  • Pattern matching allows common logic in a program, namely the conditional extraction of components from objects, to be expressed more concisely and safely.
  • The use of pattern matching in instanceof should significantly reduce the overall number of explicit casts in Java programs. Consider the following equals() methods:
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;
}
//More complex examples
if (obj instanceof String str && str.length() > 5) 
   { .. str.contains(..) .. }

Java 17 New Features (LTS)

@_tamanm

|

|

|

  • Language

  • JVM

  • API

  • Restore Always-Strict Floating-Point Semantics (JEP 306)
  • Pattern Matching for switch - 1st Preview (JEP 406)
  • Sealed Classes (JEP 409)
  • New macOS Rendering Pipeline (JEP 382)
  • macOS/AArch64 Port (JEP 391)
  • Strongly Encapsulate JDK Internals (JEP 403)
  • Enhanced Pseudo-Random Number Generators (JEP 356)
  • Foreign Function & Memory API - 1st Incubator (JEP 412)
  • Vector API - 2nd Incubator (JEP 414)
  • Context-Specific Deserialization Filters (JEP 415)
  • Deprecation/Removals

  • Remove the Experimental AOT and JIT Compiler (JEP 410)
  • Remove RMI Activation (JEP 407)
  • Deprecate the Applet API for Removal (JEP 398)
  • Deprecate the Security Manager for Removal (JEP 411)

Java 17 is the latest long term support (LTS) release after Java 11.

@_tamanm

|

|

|

JEP 409: Sealed Classes

  •  Sealed classes and interfaces restrict which other classes and interfaces may extend or implement them. Therefore they can be extended or implemented only by those classes and interfaces permitted to do so.
  • Allow the author of a class or interface to control the code responsible for implementing it.
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 { ... }
  • Provide a more declarative way than access modifiers to restrict the use of a superclass.
  • Support future directions in pattern matching by providing a foundation for analysis of patterns.

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

|

|

|

JEP 409: Sealed Classes

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.

Continue

@_tamanm

|

|

|

JEP 392: Packaging Tool

  • Provide the jpackage tool for packaging self-contained Java applications.
  • 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.

  • By default, jpackage produces a package in the format most appropriate for the system on which it is run.

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

4

Checkout

Java 18 - 21

opinionated features.

@_tamanm

|

|

|

Java 18 New Features

@_tamanm

|

|

|

  • Language

  • JVM

  • API

  • Tools

  • Pattern Matching for switch - 2nd Preview (JEP 420)
  • Reimplement Core Reflection with Method Handles (JEP 416)
  • Vector API - 3rd Incubator (JEP 417)
  • Internet-Address Resolution SPI (JEP 418)
  • Foreign Function & Memory API - 2nd Incubator (JEP 419)
  • Simple Web Server (JEP 408)
  • Code Snippets in Java API Documentation (JEP 413)
  • Deprecation/Removals

  • Deprecate Finalization for Removal (JEP 421)

Write this on macOS or Linux:

JEP 400: UTF-8 by Default

@_tamanm

|

|

|

private static void writeToFile() {
  try (FileWriter writer = new FileWriter("Jep400-example.txt");
    BufferedWriter bufferedWriter = new BufferedWriter(writer)) {
    bufferedWriter.write("مرحبا بكم في جافا جيب ٤٠٠");
  }
  catch (IOException e) {
    System.err.println(e);
  }
}
private static void readFromFile() {
  try (FileReader reader = new FileReader("Jep400-example.txt");
    BufferedReader bufferedReader = new BufferedReader(reader)) {
    String line = bufferedReader.readLine();
    System.out.println(line);
  }
  catch (IOException e) {
    System.err.println(e);
  }
}

Read it on Windows OS

You will get this:

٠رحبا ب٠٠٠٠جا٠ا ج٠ب Ù¤Ù Ù
  • Many development environments let programmers start up a rudimentary HTTP web server to test some functionality for static files.

JEP 408: Simple Web Server

@_tamanm

|

|

|

[mtaman]:~ jwebserver
Binding to loopback by default. For all interfaces use "-b 0.0.0.0" or "-b ::".
Serving /Users/mohamed_taman/Hidden Gems in Java 18/code and subdirectories on 127.0.0.1 port 8000
URL http://127.0.0.1:8000/
  • The simplest way to start the web server is with the jwebserver command. By default, this command listens to localhost on port 8000. The server also provides a file browser to the current directory.
  • New inline tag, {@snippet ...}

JEP 413: Code Snippets in Java API Documentation

@_tamanm

|

|

|

/**
  * How to read a text file with Java 8:
  * {@snippet :
  *  try (var reader = Files.newBufferedReader(path)) {
  *      String line = reader.readLine();
  *      System.out.println(line);
  *  }
  * }
  */
  • Declare code fragments to appear in the generated documentation.

Java 19 New Features

@_tamanm

|

|

|

  • Language

  • JVM

  • API

  • Record Patterns - 1st Preview (JEP 405)
  • Pattern Matching for switch - 3rd Preview (JEP 427)
  • Foreign Function & Memory API - 1st Preview (JEP 424)
  • Virtual Threads - 1st Preview (JEP 425)
  • Vector API - 4th Incubator (JEP 426)
  • Structured Concurrency - 1st Incubator (JEP 428)

Java 20 New Features

@_tamanm

|

|

|

  • Language

  • API

  • Record Patterns - 2nd Preview (JEP 432)
  • Pattern Matching for switch - 4th Preview (JEP 433)
  • Scoped Values - 1st Incubator (JEP 429)
  • Foreign Function & Memory API - 2nd Preview (JEP 434)
  • Virtual Threads - 2nd Preview (JEP 436)
  • Structured Concurrency - 2nd Incubator (JEP 437)
  • Vector API - 5th Incubator (JEP 438)

Java 21 New Features (LTS)

@_tamanm

|

|

|

  • Language

  • JVM

  • API

  • Record Patterns (JEP 440)
  • Pattern Matching for switch (JEP 441)
  • String Templates - 1. Preview (JEP 430)
  • Unnamed Patterns and Variables - 1st Preview (JEP 443)
  • Unnamed Classes and Instance Main Methods - 1st Preview (JEP 445)
  • Generational ZGC (JEP 439)
  • Prepare to Disallow the Dynamic Loading of Agents (JEP 451)
  • Sequenced Collections (JEP 431)
  • Foreign Function & Memory API - 3rd Preview (JEP 442)
  • Virtual Threads (JEP 444)
  • Scoped Values - 1st Preview (JEP 446)
  • Vector API - 6st Incubator (JEP 448)
  • Key Encapsulation Mechanism API (JEP 452)
  • Structured Concurrency - 1st Preview (JEP 453)
  • Deprecation/Removals

  • Deprecate the Windows 32-bit x86 Port for Removal (JEP 449)

This is the LTS Release after Java 17 after 2 years.

Collections in Java have always lacked an ordered approach. A new consistent methods across collections has been added as an efficient way of getting the elements in reverse order.

@_tamanm

|

|

|

JEP 431: Sequenced Collections

Collection new Methods:

SortedMap / LinkedHashMap new Methods:

SortedSet / LinkedHastSet new Method:

addFirst, addLast, getFirst, getLast, removeFirst, removeLast
reversed
reversed, sequecedKeySet, sequencedValues, sequencedEntrySet, putFirst, putLast, firstEntry, lastEntry, pollFirstEntry, pollLastEntry

Example before:

After Java 21:

var first = list.iterator().next(); 
var last = list.get(arrayList.size() - 1);
var first = list.getFirst();
var last = list.getLast();

@_tamanm

|

|

|

JEP 440: Record Pattern

Consider the following record:

public record Position(int x, int y) {}

Case 1: Record Patterns and Pattern Matching for instanceof

// Before Java 21

public void print(Object o) {
  if (o instanceof Position p) {
    System.out.printf("o is a position: %d/%d%n", p.x(), p.y());
  } else if (object instanceof String s) {
    System.out.printf("o is a string: %s%n", s);
  } else {
    System.out.printf("o is something else: %s%n", o);
  }
}
// In Java 21

public void print(Object o) {
  if (o instanceof Position(int x, int y)) {
    System.out.printf("o is a position: %d/%d%n", x, y);
  } else if (o instanceof String s) {
    System.out.printf("o is a string: %s%n", s);
  } else {
    System.out.printf("o is something else: %s%n", o);
  }
}

@_tamanm

|

|

|

JEP 440: Record Pattern

Case 2: Record Patterns and Pattern Matching for switch

// Before Java 21

public void print(Object o) {
  switch (o) {
    case Position p -> System.out.printf("o is a position: %d/%d%n", p.x(), p.y());
    case String s   -> System.out.printf("o is a string: %s%n", s);
    default         -> System.out.printf("o is something else: %s%n", o);
  }
}
// In Java 21

public void print(Object o) {
  switch (o) {
    case Position(int x, int y) -> System.out.printf("o is a position: %d/%d%n", x, y);
    case String s               -> System.out.printf("o is a string: %s%n", s);
    default                     -> System.out.printf("o is something else: %s%n", o);
  }
}

Continue

@_tamanm

|

|

|

JEP 440: Record Pattern

Case 3: Nested Record Patterns

// Before Java 21

public void print(Object o) {
  switch (o) {
    case Path p ->
            System.out.printf("o is a path: %d/%d -> %d/%d%n", 
                    p.from().x(), p.from().y(), p.to().x(), p.to().y()); 
    // other cases
  }
}
// In Java 21

public void print(Object o) {
  switch (o) {
    case Path(Position from, Position to) ->
            System.out.printf("o is a path: %d/%d -> %d/%d%n", 
                    from.x(), from.y(), to.x(), to.y());
    // other cases
  }
}

Continue

Consider the following record:

public record Path(Position from, Position to) {}

@_tamanm

|

|

|

JEP 440: Record Pattern

Case 3: Nested Record Patterns

// In Java 21

public void print(Object o) {
  switch (o) {
    case Path(Position(int x1, int y1), Position(int x2, int y2)) ->
            System.out.printf("o is a path: %d/%d -> %d/%d%n", x1, y1, x2, y2);
    // other cases
  }
}

Continue

Secondly, we can also use a nested record pattern as follows:

@_tamanm

|

|

|

JEP 440: Record Pattern

Case 3: Nested Record Patterns

public sealed interface Position permits Position2D, Position3D {}
public record Position2D(int x, int y) implements Position {}
public record Position3D(int x, int y, int z) implements Position {}
public record Path<P extends Position>(P from, P to) {}

Continue

The Real Power of Record Patterns

public void print(Object o) {
  switch (o) {
    case Path(Position2D from, Position2D to) ->
            System.out.printf("o is a 2D path: %d/%d -> %d/%d%n",
                    from.x(), from.y(), to.x(), to.y());
                    
    case Path(Position3D from, Position3D to) ->
            System.out.printf("o is a 3D path: %d/%d/%d -> %d/%d/%d%n",
                    from.x(), from.y(), from.z(), to.x(), to.y(), to.z());
    // other cases
  }
}

@_tamanm

|

|

|

JEP 440: Record Pattern

Case 3: Nested Record Patterns

Continue

Without record patterns, we would have to write the following code:

public void print(Object o) {
  switch (o) {
    case Path p when p.from() instanceof Position2D from 
                  && p.to() instanceof Position2D to ->
            System.out.printf("o is a 2D path: %d/%d -> %d/%d%n",
                    from.x(), from.y(), to.x(), to.y());
    
    case Path p when p.from() instanceof Position3D from 
                  && p.to() instanceof Position3D to ->
            System.out.printf("o is a 3D path: %d/%d/%d -> %d/%d/%d%n",
                    from.x(), from.y(), from.z(), to.x(), to.y(), to.z());
    // other cases
  }
}

@_tamanm

|

|

|

JEP 441: Pattern Matching for switch

With pattern matching for switch, we could write the following code:

// Before Java 21

Object obj = getObject();

if (obj instanceof String s && s.length() > 5)  System.out.println(s.toUpperCase());
else if (obj instanceof String s)               System.out.println(s.toLowerCase());
else if (obj instanceof Integer i)              System.out.println(i * i);
else if (obj instanceof Position(int x, int y)) System.out.println(x + "/" + y);
// In Java 21

Object obj = getObject();

switch (obj) {
  case String s when s.length() > 5 -> System.out.println(s.toUpperCase());
  case String s                     -> System.out.println(s.toLowerCase());
  case Integer i                    -> System.out.println(i * i);
  case Position(int x, int y)       -> System.out.println(x + "/" + y);
  default                           -> {}
}

@_tamanm

|

|

|

JEP 430: String Templates (Preview)

// Before 

int a = ...;
int b = ...;

String concatenated = a + " times " + b + " = " + a * b;
String format       = String.format("%d times %d = %d", a, b, a * b);
String formatted    = "%d times %d = %d".formatted(a, b, a * b);
// In Java 21 

int a = ...;
int b = ...;

String interpolated = STR."\{a} times \{b} = \{a * b}";

String interpolated = STR."\{a} times \{b} = \{Math.multiplyExact(a, b)}";
// In Java 21 

String dateMessage = STR."Today's date: \{
        LocalDate.now().format(
                  // We could also use DateTimeFormatter.ISO_DATE
                  DateTimeFormatter.ofPattern("yyyy-MM-dd")
        )}";
// In Java 21 

int    httpStatus   = ...;
String errorMessage = ...;

String json = STR."""
    {
      "httpStatus": \{httpStatus},
      "errorMessage": "\{errorMessage}"
    }""";

@_tamanm

|

|

|

JEP 443: Unnamed Patterns and Variables (Preview)

// Example 1: Exceptions – here, we don’t use e:
try {
  int number = Integer.parseInt(string);
  
} catch (NumberFormatException e) {
  System.err.println("Not a number");
}

// Example 2: Map.computeIfAbsent() – this time, we don’t use k:
map.computeIfAbsent(key, k -> new ArrayList<>()).add(value);
// In Java 21

// Here is the exception example with an unnamed variable:
try {
  int number = Integer.parseInt(string);
  
} catch (NumberFormatException _) {
  System.err.println("Not a number");
}

// And Map.computeIfAbsent()
map.computeIfAbsent(key, _ -> new ArrayList<>()).add(value);

Unnamed Variable

@_tamanm

|

|

|

JEP 443: Unnamed Patterns and Variables (Preview)

if (object instanceof Position(int x, int y)) {
  System.out.println("object is a position, x = " + x);
}

Unnamed Pattern Variable

Continue

if (object instanceof Position(int x, int _)) {
  System.out.println("object is a position, x = " + x);
}
if (object instanceof Position(int x, _)) {
  System.out.println("object is a position, x = " + x);
}

Unnamed Pattern

@_tamanm

|

|

|

JEP 443: Unnamed Patterns and Variables (Preview)

Continue

switch (obj) {
  case Byte    b -> System.out.println("Integer number");
  case Short   s -> System.out.println("Integer number");
  case Integer i -> System.out.println("Integer number");
  case Long    l -> System.out.println("Integer number");

  case Float  f -> System.out.println("Floating point number");
  case Double d -> System.out.println("Floating point number");

  default -> System.out.println("Not a number");
}
switch (obj) {
  case Byte    _ -> System.out.println("Integer number");
  case Short   _ -> System.out.println("Integer number");
  case Integer _ -> System.out.println("Integer number");
  case Long    _ -> System.out.println("Integer number");

  case Float  _ -> System.out.println("Floating point number");
  case Double _ -> System.out.println("Floating point number");

  default -> System.out.println("Not a number");
}
switch (obj) {
  case Byte _, Short _, Integer _, Long _ -> System.out.println("Integer number");
  case Float _, Double _                  -> System.out.println("Floating point number");

  default -> System.out.println("Not a number");
}

@_tamanm

|

|

|

JEP 445: Unnamed Classes and Instance Main Methods (Preview)

public class HelloWorld {
  public static void main(String[] args) {
    System.out.println("Hello world!");
  }
}
void main() {
  System.out.println("Hello world!");
}
final String HELLO_TEMPLATE = "Hello %s!";

void main() {
  System.out.println(hello("world"));
}

String hello(String name) {
  return HELLO_TEMPLATE.formatted(name);
}

The "Unnamed Class"

5 Resources

@_tamanm

|

|

|

Questions?

Thanks for listening! 🧡

@_tamanm

|

|

|

Java 11-21 Evolution

By Mohamed Taman

Java 11-21 Evolution

Is Java a tired relic of the past that’s ready for the programming language museum, or is it still a vital weapon in every good developer’s arsenal? This session will examine the evolution of Java from 11 to 21 and the impactful features that make your life easier. Java has been a powerful and widely used programming language for over two decades. Java has transformed the software development landscape and remains dominant in modern software development. With every year seeing new young pretenders to the crown emerge, is Java still relevant and worth learning in 2023? We will look at Java’s evolution and impact on software development to show why it matters more than ever.

  • 280