Java 17: Here's a juicy update on everything that's new - Ideas2IT

Java 17: Here’s a juicy update on everything that’s new

Share This

With the recent release of the Java 17 LTS release, many new language features have been added to JDK. Let’s explore each of these new features in JDK17 in brief.

Sealed Classes — JDK 17 JEP 409

Added as a preview in JDK16, sealed classes and interfaces restrict which other classes need to implement or extend them. It allows the author of the class or interfaces to control which code is responsible for extending or implementing them.

A class is sealed by adding a sealed modifier to its declaration and the permits clause specifies classes that are permitted to extend a sealed class, sealed class and permitted subclass needs to be in the same module. If the module is not defined, it needs to be the same package. subclasses extending sealed class can be either final or another sealed class or non-sealed classes, like in the below example.

 

package com.domain.shapes;

public abstract sealed class Shape permits Circle,Square, Rectangle {
}

public final class Circle extends Shape {
}

public non-sealed class Square extends Shape {
}

public sealed class Rectangle extends Shape permits TransparentRectange,OpaqueRectangle {
}

public final class OpaqueRectangle extends Rectangle {
}

public final class TransparentRectange extends Rectangle {
}

 

Text Blocks — JDK 17 JEP 378

Multi-line String literals can be represented in quotes without having to deal with most escape sequences.

Consider the below example of populating an HTML string in a string variable, before text blocks. We need to escape with new line escapes and concatenation for below multiple lines spanning string, manually processing the below string can lead to error-prone.

 

String html = "<html>n" +
        "<body>n"+
        "<p>Test</p>n"+
        "</body>n"+
        "</html>n";

 

With text block in JDK17, the above string can be rewritten with text block like below, it is defined with three double quotes and it can span in multiple lines. The text block automatically formats the strings in a predictable way and gives the developer control over the format needed.

 

String html = """
        <html>
            <body>
                <p>Test</p>
            </body>
        </html>
        """;

 

Pattern Matching for instanceof — JDK 16 JEP 394

Pattern matching allows the condition extraction of components from objects to be expressed more concisely and safely.

Let’s consider the below example, here we are first testing whether obj is a string, then the declaration of string variables, and then type casting obj to string into variables.

 

if(obj instanceof String) {
        String s = (String) obj;
}

With JDK17, pattern matching for the instanceof feature, we can reduce the code to a single line like below, i.e, the variable s is created only when obj is instance of String, and type casting obj to string happens automatically.

 

if(obj instanceof String s) {
        //use s
} else {
        // s is out of scope here
}

 

Switch Expressions (JDK 14 JEP 361)

This extends the switch so it can be used as a switch statement or expression. Both forms can be used.

 

raditional case ... : labels //with fall through
New        case ... -> labels //without fall through

 

New statement “yield” has been added for yielding (Returning) a value from switch expression.

For example, with traditional switch expression in Java.

 

int  numLetters;
switch(day)
  {
      case MONDAY:
      case FRIDAY:
      case SUNDAY:
          numLetters = 6;
          break;
      case TUESDAY:
          numLetters = 7;
          break;
      case THURSDAY:
      case SATURDAY:
          numLetters = 8;
      case WEDNESDAY:
          numLetters = 9;
          break;
      default:
          throw new IllegalArgumentException(" Not a day: " + day);
  }
  return numLetters;

 

Using switch expressions, it returns a value. No extra variables are needed as in the above case and also multiple constants are separated by a comma and can be defined in each branch.

 

return switch(day) {
    case   MONDAY, FRIDAY, SUNDAY -> 6;
    case   TUESDAY -> 7;
    case   THURSDAY, SATURDAY -> 8;
    case   WEDNESDAY -> 9;
   }

 

Pattern Matching for switch Preview — JDK 17 JEP 406

Pattern matching on switch allows an expression to be tested against a number of patterns, each with specific action.

Current switch statements allow working on a few types — Numeric, Enum, and Strings. If we want to use patterns to check for certain conditions, then the chain of if-else condition blocks is used as shown below.

 

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);
    }
    return formatted;
}

 

With the new feature “pattern matching for switch”, a chain of if-else conditions is replaced with switch expressions as it checks for the pattern.

 

static String formatter(Object o){
    return switch(o){
        case null      -> "null";
        case Integer i -> String.format("int %d", i);
        case Long l    -> String.format("long %d", l);
        default        -> o.toString();
    };
}

 

Records — JDK 16 JEP 395

Records provide a compact syntax for declaring classes that are transparent holders for shallowly immutable data.

JDK 16 had a records feature, for developers who needed to deal with boilerplate codes (such as getter/setter, constructor, equals, and hashcode implementations).

Records are built on components, components are compiled as final fields like below, they are only fields that can have records, but we cannot add any instance fields in records.

 

record range(int start, int end){}

 

Record is final class, no state can be added to record class through inheritance.

 

 

 

 

Previously in JDK 16, the final class would have been written using the below approach.

 

public final class point{
    final int x;
    final int y;

    public point(int x, int y) {
        this.x = x;
        this.y = y;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        point point = (point) o;
        return x == point.x && y == point.y;
    }

    @Override
    public int hashCode() {
        return Objects.hash(x, y);
    }

    @Override
    public String toString() {
        return "point{" +
                "x=" + x +
                ", y=" + y +
                '}';
    }

    public int x(){
        return x;
    }

    public int y(){
        return y;
    }
}

 

With JDK17, instead of class, we say records, so that the compiler can make sure our records will get all required methods without having to explicitly define it. Records are not just eliminating the boilerplate codes. Records represent improvements in the platform.