Get Your Copy of The CXO's Playbook for Gen AI: Practical Insights From Industry Leaders.  Download Now >
Back to Blogs

Java 17 New Features: Here's a Juicy Update in JDK17

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.

  • Provides a more declarative way than access modifiers to restrict the use of superclass.
  • Support future enhancements in pattern matching to avoid exhaustive analysis of patterns.

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 a permitted subclass need to be in the same module. If the module is not defined, it needs to be in the same package. Subclasses extending sealed classes can be either final, 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 the below multiple line spanning string, manually processing the below string can be error-prone.

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

With a text block in JDK17, the above string can be rewritten with a 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 conditional 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 a string into variables.

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

With JDK17, pattern matching for the instance feature, we can reduce the code to a single line like below, i.e, the variable s is created only when obj is an 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 the 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 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 the switch allows an expression to be tested against a number of patterns, each with a specific action.

  • allow patterns to appear in case labels
  • introduces case null statement

Current switch statements allow working on a few types: numeric, enum, and string. 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 the only fields that can have records, but we cannot add any instance fields to records.

record range(int start, int end){}

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

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

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 them. Records are not just eliminating boilerplate codes. Records represent improvements in the platform.

Ideas2IT Team

Connect with Us

We'd love to brainstorm your priority tech initiatives and contribute to the best outcomes.