Monday, June 20, 2022

Java_17_Features - ALL

 Table of Contents 

2.1. Restore Always-Strict Floating-Point Semantics

This JEP is targeted towards scientific calculations which involve floating-point numbers. To guarantee the same calculation result on all platforms, we have been using the keyword strictfp.

The strictfp modifier accomplishes this by representing all intermediate values as IEEE single-precision and double-precision values. But due to hardware heating issues, it became optional in JDK 1.2.

Today, as the hardwares have evolved and those heating issues are fixed, default floating-point semantics have been changed to consistently strict.

With this change, we do not need to use strictfp keyword, anymore.

2.2. Pattern Matching for Switch (Preview)

This change adds pattern matching for switch statements and expressions. Since this is a preview feature, we need to use –enable-preview option to enable it.

2.2.1. Better instanceof checking with Switch Statements

Traditionally, if we had to write a code that checks the instance type and perform some logic, it was the way

Traditional way of checking instance type Object o; if (o instanceof String) { String s = (String) o; String.format("String %s", s) } else if (o instanceof Integer) { Integer i = (Integer) o; String.format("int %d", i) } else if (o instanceof Double) { Double d = (Double) o; String.format("double %f", d) }

In Java 17, we can rewrite the whole expression in the following manner;:

New Approach of Type Checking Object o; switch (o) { case Integer i -> String.format("int %d", i); case Double d -> String.format("double %f", d); case String s -> String.format("String %s", s); default -> o.toString(); }

2.2.2 Handling null Values

Traditional switch statements throw NullPointerException if the selector expression evaluates to null.

From Java 17, we can check for null values as the separate case itself.:

Before Java 17
if (s == null) { System.out.println("oops!"); return; } switch (s) { case "Foo", "Bar" -> System.out.println("Great"); default -> System.out.println("Ok"); }

In Java 17
switch (s) { case null -> System.out.println("Oops"); case "Foo", "Bar" -> System.out.println("Great"); default -> System.out.println("Ok"); }

2.2.3. No explicit null checks are needed

Traditional switch statements throw NullPointerException if the selector expression evaluates to null. With this change, we can check for such null expressions as the separate case itself.:

Before Java 17 if (s == null) { System.out.println("oops!"); return; } switch (s) { case "Foo", "Bar" -> System.out.println("Great"); default -> System.out.println("Ok"); }

In Java 17 switch (s) { case null -> System.out.println("Oops"); case "Foo", "Bar" -> System.out.println("Great"); default -> System.out.println("Ok"); }

2.2.4. Improved instanceof checking

Before Java 16, if we had to write a code that checks the instance type and perform some logic, it was the way:

Before Java 16 Object o; if (o instanceof String) { String s = (String) o; String.format("String %s", s) } else if (o instanceof Integer) { Integer i = (Integer) o; String.format("int %d", i) } else if (o instanceof Double) { Double d = (Double) o; String.format("double %f", d) }

Java 17 takes it to the next level with switch expression. Now we can rewrite the above code as:

In Java 17 Object o; switch (o) { case Integer i -> String.format("int %d", i); case Double d -> String.format("double %f", d); case String s -> String.format("String %s", s); default -> o.toString(); }

While the above changes have been made, it has been ensured that all of the old
switch expressions must continue to work.

You can see all the changes to switch expressions in the JEP webpage.

2.3. Sealed Classes

In Java, by default, there has been no restriction on a class which public interfaces it can implement. From Java 15, we can declare a class or interface sealed class or sealed interface using the modifier sealed.

Sealed classes and interfaces restrict which other classes or interfaces may extend or implement them.:

public sealed class Account permits CurrentAccount, SavingAccount, LoanAccount { }

It was a preview feature in Java 15 and Java 16. In Java 17, it has become a standard feature with no changes to what was available in Java 16.

2.3.1. sealed modifier and permits keyword

The sealing of a class or interface can be done using the modifier sealed in the declaration time. Then, after any extends and implements clauses, the permits clause specifies the classes that are permitted to extend the sealed class.

The reserved keyword permits lists all the classes that can extend the sealed class directly. The listed subclasses can either be final, non-sealed, or sealed.

For example, the following declaration of Account specifies three permitted subclasses:

sealed class Account permits CurrentAccount, SavingAccount, LoanAccount { } final class CurrentAccount extends Account {} non-sealed class SavingAccount extends Account {} sealed class LoanAccount extends Account permits HomeloanAccount, AutoloanAccount {} final class HomeloanAccount extends LoanAccount{} final class AutoloanAccount extends LoanAccount{}

Here, we are permitted only 3 classes the permission to extend the Account class. No other class class can extend the Account class.

Similarily, only HomeloanAccount and AutoloanAccount are only two supported account types for LoanAccount.

2.3.2 finalsealed and non-sealed Subclasses

The permitted subclasses must have exactly one of the following modifiers to describe how it continues the sealing initiated by its superclass:

  • final class cannot be extended further.
  • sealed class can only be extended by its permitted subclasses.
  • non-sealed class can be extended by unknown subclasses as well. A sealed class cannot force the sealing behavior to its permitted subclasses.

2.3.4 Packaging of sealed classes

  • The sealed class and its subclasses, all must be inside the same module.
  • If the given module is a unnamed module then all the classes must be inside the same package.
  • If the permitted classes are written in the same .java file then we can omit the permits keyword.
  • Account.java public sealed class Account {} //Omits 'permits' keyword final class CurrentAccount extends Account {} non-sealed class SavingAccount extends Account {} sealed class LoanAccount extends Account {} //Omits 'permits' keyword final class HomeloanAccount extends LoanAccount{} final class AutoloanAccount extends LoanAccount{}

2.3.4. Records as sealed classes

We can have a record class in the permits clause of a sealed class or interface.

Record classes are implicitly final and can be used as subclasses of a sealed class.

2.3.4. APIs Support

Java has added two new methods to support the usage of sealed classes::

java.lang.constant.ClassDesc[] permittedSubclasses() - It returns an array of ClassDesc objects where each object in array represent a subclass of the sealed class. boolean isSealed() – It returns true if the given class or interface is sealed.

2.3.5. Sealed Interfaces

There are not many differences between how a sealed class is defined vs. how a sealed interface is defined. Like sealed classes, to seal an interface, add the sealed modifier to its declaration.

Then, after any extends clause, add the permits clause which specifies the classes that can implement the sealed interface and the interfaces that can extend the sealed interface.:

IReport.java public sealed interface IReport permits Printable, Formattable, ExcelReport, PdfReport { } non-sealed interface Printable extends IReport {} non-sealed interface Formattable extends IReport {} non-sealed class ExcelReport implements IReport {} non-sealed class PdfReport implements IReport {}

Sealed interfaces follow the same declaration rules as sealed classes.

2.3.6 When to use sealed classes?

Remember the use of instanceof operator to test whether the object is an instance of the specified class. Generally, we checked all the possible class types and, at last, in else block, we have to write some code if the given instance doesn’t match any class type.

Also, there is no guarantee that another developer will add one more class to the hierarchy and add the type check in the given instanceof checks. Sealed classes can impose this restriction on the hierarchies at the language level.

With the future direction of instanceof operator and pattern matching, the default block or else block will not be needed.

As soon as a new subclass is added to a sealed class, by using type test patterns, the compiler will be able to check that every permitted subclass is covered.

2.4. Enhanced Pseudo-Random Number Generators

2.4.1. RandomGenerator

This JEP introduced a new interface called RandomGenerator which aims to make future pseudorandom number generator (PRNG) algorithms easier to implement or use.

With the legacy PRNG classes RandomThreadLocalRandom, and SplittableRandom, it is difficult to replace any of them in an application with some other algorithm because they did not have any supertype to support runtime changes.

With the introduction of RandomGenerator an interface, we can inject any implementing generator class where the client uses RandomGenerator type in the application code. The new classes are:

  • SplittableGenerator – can be split into two objects (the original one and a new one) each of which obeys that same protocol.
  • JumpableGenerator – can easily jump forward, by a moderate amount.
  • LeapableGenerator – can easily not only jump but also leap forward, by a large amount.
  • ArbitrarilyJumpableGenerator – can easily jump forward, by an arbitrary amount, to a distant point in the state cycle.
  • StreamableGenerator – augments the RandomGenerator interface to provide methods that return streams of RandomGenerator objects.

Also, legacy random classes, such as Java.util.RandomSplittableRandomThreadLocalRandom and SecureRandom now extend the new RandomGenerator interface.

2.4.2. RandomGeneratorFactory

The RandomGeneratorFactory provides methods for selecting random number generator algorithms. We can choose a particular algorithm by its name and use it for generating random numbers.

The default algorithm is L32X64MixRandom. Following is the list of all supported algorithms:

  • L128X1024MixRandom
  • L128X128MixRandom
  • L128X256MixRandom
  • L32X64MixRandom
  • L64X1024MixRandom
  • L64X128MixRandom
  • L64X128StarStarRandom
  • L64X256MixRandom
  • Random (Legacy)
  • SecureRandom (Legacy)
  • SplittableRandom (Legacy)
  • Xoroshiro128PlusPlus
  • Xoshiro256PlusPlus

The Legecy group represents the old PRNGs. Please note that none of the new implementations are thread-safe while both Java.util.Random and Java.security.SecureRandom is.:

RandomGeneratorFactory factory = RandomGeneratorFactory.of("SecureRandom") RandomGenerator random = factory.create(200L); //get random numbers randomGenerator.nextDouble();

2.5. Deprecate the Applet API for Removal

Most of the web browsers have already removed the support for Applets due to security concerns. In Java 9, Applet API was marked deprecated.

Since it has become irrelevant today, Java 17 has marked it for removal.

2.6. Strongly Encapsulate JDK Internals

This JEP strongly encapsulates all internal elements of the JDK, except for critical internal APIs such as sun.misc.Unsafe.

From Java 9 to Java 16, developers were able to access the JDK internal APIs using the flag –illegal-access. Java 17 will ignore the flag, and if the flag is present.

The console will display a message informing the discontinuation of the flag.

2.7. Remove RMI Activation

Remove the Remote Method Invocation (RMI) Activation mechanism while preserving the rest of RMI. It is obsolete and has been deprecated in Java 15.

2.8. Remove the Experimental AOT and JIT Compiler

This JEP removes the experimental Java-based ahead-of-time (AOT) and just-in-time (JIT) compiler introduced in Java 9 and Java 10.

The Graal compiler was made available as an experimental JIT compiler in JDK 10 via JEP 317. Since they were introduced, there was little use of these experimental features, and the effort required to maintain and enhance them was significant.

The developers can still leverage these features using GraalVM.

2.9. Conclusion

Java 17 has lots of exciting features and a long road of support and commitment. It removes many of the already deprecated APIs from Java.

There is significantly less chance to find any applications which are still using those deprecated features, still, developers must be careful to thoroughly check the application code and dependencies before migrating to Java 17.

As Java 17 is an LTS release, and major popular frameworks (e.g., Spring 6 and Spring Boot 3) will be supporting the new features, it is better to plan for Java 17 migration.

You may also like

Kubernetes Microservices
Python AI/ML
Spring Framework Spring Boot
Core Java Java Coding Question
Maven AWS