In September 2021, the "Java Platform, SE 17 Development Kit (JDK 17)," or Java 17, was released containing new features, including enhancements on many functional areas. In this release, Java continues with its evolution.
Here are some of the updates:
- Sealed classes are standardized.
- Always-strict floating-point semantics are restored.
- Switch statements are enriched with better pattern matching.
- Pseudorandom number generators are upgraded.
- Support for context-specific and dynamically-selected deserialization filters is provided.
- RMI Activation mechanism is removed.
- Security Manager and the famous Applet API are deprecated and will be removed/scheduled for removal.
Unlike previous releases, strong encapsulation of an Implementation's modules is now always required with no option to disable it. You can read the official Java SE 17 (JSR 392) final release specification for more detailed information. (Release Notes)
This version of the language is now available for production use.
Let's review and try some of the new features. You can find the code examples we'll use in the article in the official documentation. Some of the features included in this release are implemented on a preview basis so that the community can get familiar with them before they become permanent in some later releases; this is a common practice when releasing new Java features.
Pattern Matching for Switch (Language Level)
Switch expressions and statements are enriched with many pattern matching expressions (regular expressions) to express complex data-oriented queries concisely and safely.
Some of the goals of this feature are the following:
- Switch expressions and statements to be more applicable and expressive in a way that patterns could appear in case labels.
The instanceof operator can now take a type pattern and perform a type matching, bringing simplicity. Let us review this through code examples.
If we use this, the switch statement will look like the following:
Now, let’s apply pattern matching instead of constants. The statements of this switch are clear.
- Null Pointer Exception in the switch can now be relaxed when needed.
When it comes to Null values in switch statements and expressions, a null pointer exception used to be thrown, and the testing needed to be done outside of the switch.
Now, it's possible to have a selector expression of any type and case labels with type patterns, so it would be better if the null test is inside the switch:
In order to maintain backward compatibility, the default label does not match a null selector.
There is a possibility now for the matching of the null selector to be along with another selector, like in the following example, when both null case and String case are producing the same result:
- Introduction to two new patterns - guarded patterns and parenthesized patterns.
Parenthesized patterns resolve some parsing ambiguities, while guarded patterns allow the matching logic to be refined with arbitrary boolean expressions.
To avoid errors during execution in the case of allowing labels refinement, an action is taken, called guard (in other programming languages). The switch is enhanced instead of using just constants as case labels to use patterns and the constants.
Instead of extending the functionality of the case labels, we can extend the patterns. We can add a new kind of pattern called a guarded pattern, written p&&b, that allows a pattern p to be refined by an arbitrary boolean expression b. With this approach, we can use the arrow-style rule.
Let's see the change in the following examples:
Our code now looks more readable and beautified, doing exactly what it needed to do but more elegantly.
For future work on this subject, it is stated that the switch still does not support the primitive types of the Java data types; boolean, float, and double, but this could be added in future releases.
It is expected that general classes will be able to declare deconstruction patterns to specify how they can be matched against. For example, suppose we have a hierarchy of Expression with subtypes for IntExpression (containing a single int), AddExpression and MulExpression (containing two Expressions), and NegExpression (containing a single Expression).
In that case, we can match against an Expression and act on the specific subtypes all in one step:
Adding AND and OR patterns would be a great idea, in order to allow more expressivity for case labels with patterns.
Find more information about this feature in JEP 406.
Sealed Classes (Language Level)
A sealed class or interface can be extended or implemented only by those classes and interfaces that are allowed to do so. This idea of sealed classes was initially delivered in JDK 15 but only as a preview feature.
The whole general idea and usage of this feature is to:
- provide a more declarative way than access modifiers for restricting the usage of a superclass
- allow the author of the class or the interface to control which code is responsible for its implementation
- provide a foundation for extensive analysis of patterns in support of future pattern matching directions
Let it be stated that this feature does not have the goal to change “final” in any way nor to provide new forms of access control such as "friends".
The motivation behind this is that not everything is perfectly represented with the object-oriented data model of inheritance hierarchies of classes and interfaces when modeling the real-world data used by modern applications. There are cases where this aspect can be usefully tamed. Such cases are the Enum classes that are used for modeling a situation where a class has only a fixed number of instances. The following is an Enum example:
There are times when we want to model a fixed set of types of values. This can be done by using a class hierarchy not as a mechanism for code inheritance and reuse but, rather, as a way to list types of values. If we model these values from the Enum above in the astronomical domain, the code will look like this:
This hierarchy does not reflect the important domain knowledge that there are only three kinds of celestial objects in our model. In this case, restricting the set of subclasses or subinterfaces can simplify the modeling. A superclass should not be widely extensible but could be widely accessible. The author of that class can express his intention for developing that superclass with a given set of subclasses. The superclass should not unnecessarily constrain its subclasses by forcing them to be final or to be prevented from defining their own state.
A class in Java 17 is sealed by applying the sealed modifier to its declaration. Right after that, we should define the classes that are permitted to extend that sealed class or interface by using the keyword permit. By using the same example from above with the celestial objects, the final result would look like this:
The classes specified by permits must be located near the superclass: in the same module or the same package.
An example of how sealed classes might be used in the JDK is in the java.lang.constant package:
Find more information about this feature in JEP 409.
Restore Always-Strict Floating-Point Semantics (Language Level)
To ease development of numerically-sensitive libraries, including java.lang.Math and java.lang.StrictMath, and provide more regularity in a tricky aspect of the platform, always-strict floating-point semantics are restored.
Floating-point operations will now be consistently strict, rather than having both strict floating-point semantics (strictfp) and subtly different default floating-point semantics. This restores the original floating-point semantics to the language and VM, matching the semantics before the introduction of strict and default floating-point modes in Java Standard Edition 1.2. Find more information about Java 17 download and this feature in JEP 306.
Deprecate the Security Manager for Removal (Security Level)
Due to a security reason, the security manager is deprecated to be removed from future releases. It was first added in Java 1.0. but was rarely used for securing the server-side code. It has not been the primary means of securing client-side code for a while. This feature is not frequently used in production and for good reasons. The lack of use in production is due to its many vulnerabilities like brittle permission, complex programming, and poor performance.
Context-Specific Deserialization Filters (Library Level)
Applications are allowed to configure context-specific and dynamically selected filters for deserialization, using the JVM-wide filter factory invoked to select a filter for each serialization operation. Serialization of untrusted data is ultimately a dangerous activity. Most often, the data from the stream in Java 17 is received from an untrusted or unauthenticated client. To protect the code from serialization attacks, the instances of arbitrary classes deserializing need to be prevented while preventing the execution of their methods.
The use of deserialization filters (introduced in Java 9) is to enable validation of incoming data streams before deserializing it. Validation code logic is supplied as a java.io.ObjectInputFilter when deserialization stream is created. Let there be noted that their goal is not to define policies for deserialization filter selection nor to define a mechanism for the configuration or distribution of filters.
Find more information about this new Java 17 feature in JEP 415.
Enhanced Pseudo-Random Number Generators (Library Level)
New interfaces and implementations for pseudorandom number generators (PRNGs) were added and the goals of this feature are:
- Elimination of code duplications in Existing PRNG classes.
- Easier use of various PROG algorithms interchangeably in applications.
- Preserving existing behavior of java.util.
- Random class and better support for stream-based programming by providing streams of PRNG objects.
Find more information about this feature in JEP 356.
Strongly Encapsulate JDK Internals (Library Level)
The main goals of this feature are a smooth upgrade (for Java developers) from using internal elements to using standard APIs to future Java releases, JDK security, and maintainability improvements. Strong encapsulation of the internal JDK elements, except for the critical APIs (sun.misc.Unsafe). In JDK9 through JDK 16 it was possible to relax the strong encapsulation of the internal elements via a single command-line option, this is no longer possible from this Java version on. You can find more information about this feature in JEP 403.
Deprecate the Applet API for Removal (Library Level)
Sad news for everyone who started programming Java Applets a while ago: this API has reached its end. All web-browsers vendors have removed the support for Java browser plug-ins or announced that they are planning to do so. The Applet API is officially deprecated for removal. Find more information about this feature in JEP 398.
Remove RMI Activation (Library Level)
Because the RMI Activation mechanism is obsolete and out-of-date, this version removes it while the rest of the RMI is preserved. It was deprecated for removal by JEP 385 in Java SE 15. Find more information about this feature in JEP 407.
The release date of Java 18 is March 22, 2022. It will bring new features and preview features. Stay tuned for Java 17 vs Java 18 analysis.