Java Se Language Updates
Java Se Language Updates
Release 21
F79543-03
January 2025
Java Platform, Standard Edition Java Language Updates, Release 21
F79543-03
This software and related documentation are provided under a license agreement containing restrictions on use and
disclosure and are protected by intellectual property laws. Except as expressly permitted in your license agreement or
allowed by law, you may not use, copy, reproduce, translate, broadcast, modify, license, transmit, distribute, exhibit,
perform, publish, or display any part, in any form, or by any means. Reverse engineering, disassembly, or decompilation
of this software, unless required by law for interoperability, is prohibited.
The information contained herein is subject to change without notice and is not warranted to be error-free. If you find
any errors, please report them to us in writing.
If this is software, software documentation, data (as defined in the Federal Acquisition Regulation), or related
documentation that is delivered to the U.S. Government or anyone licensing it on behalf of the U.S. Government, then
the following notice is applicable:
U.S. GOVERNMENT END USERS: Oracle programs (including any operating system, integrated software, any
programs embedded, installed, or activated on delivered hardware, and modifications of such programs) and Oracle
computer documentation or other Oracle data delivered to or accessed by U.S. Government end users are "commercial
computer software," "commercial computer software documentation," or "limited rights data" pursuant to the applicable
Federal Acquisition Regulation and agency-specific supplemental regulations. As such, the use, reproduction,
duplication, release, display, disclosure, modification, preparation of derivative works, and/or adaptation of i) Oracle
programs (including any operating system, integrated software, any programs embedded, installed, or activated on
delivered hardware, and modifications of such programs), ii) Oracle computer documentation and/or iii) other Oracle
data, is subject to the rights and limitations specified in the license contained in the applicable contract. The terms
governing the U.S. Government's use of Oracle cloud services are defined by the applicable contract for such services.
No other rights are granted to the U.S. Government.
This software or hardware is developed for general use in a variety of information management applications. It is not
developed or intended for use in any inherently dangerous applications, including applications that may create a risk of
personal injury. If you use this software or hardware in dangerous applications, then you shall be responsible to take all
appropriate fail-safe, backup, redundancy, and other measures to ensure its safe use. Oracle Corporation and its
affiliates disclaim any liability for any damages caused by use of this software or hardware in dangerous applications.
Oracle®, Java, MySQL, and NetSuite are registered trademarks of Oracle and/or its affiliates. Other names may be
trademarks of their respective owners.
Intel and Intel Inside are trademarks or registered trademarks of Intel Corporation. All SPARC trademarks are used
under license and are trademarks or registered trademarks of SPARC International, Inc. AMD, Epyc, and the AMD logo
are trademarks or registered trademarks of Advanced Micro Devices. UNIX is a registered trademark of The Open
Group.
This software or hardware and documentation may provide access to or information about content, products, and
services from third parties. Oracle Corporation and its affiliates are not responsible for and expressly disclaim all
warranties of any kind with respect to third-party content, products, and services unless otherwise set forth in an
applicable agreement between you and Oracle. Oracle Corporation and its affiliates will not be responsible for any loss,
costs, or damages incurred due to your access to or use of third-party content, products, or services, except as set forth
in an applicable agreement between you and Oracle.
Contents
Preface
Audience vi
Documentation Accessibility vi
Diversity and Inclusion vi
Related Documents vi
Conventions vi
3 Preview Features
iii
4 String Templates
Basic Usage of String Templates 4-1
Embedded Expressions in String Templates 4-2
Multiline String Templates 4-4
The FMT Template Processor 4-5
The RAW Template Processor 4-6
Creating a Template Processor 4-7
Creating a Template Processor that Returns JSON Objects 4-7
Creating a Template Processor that Safely Composes and Runs Database Queries 4-10
Creating a Template Processor that Simplifies the Use of Resource Bundles 4-12
6 Sealed Classes
7 Pattern Matching
Pattern Matching for the instanceof Operator 7-1
Scope of Pattern Variables 7-3
Pattern Matching for switch Expressions and Statements 7-4
Selector Expression Type 7-5
When Clauses 7-5
Qualified enum Constants as case Constants 7-6
Pattern Label Dominance 7-8
Type Coverage in switch Expressions and Statements 7-9
Inference of Type Arguments in Record Patterns 7-12
Scope of Pattern Variable Declarations 7-12
Null case Labels 7-15
Record Patterns 7-16
Unnamed Patterns and Variables 7-18
8 Record Classes
iv
9 Switch Expressions
10 Text Blocks
v
Preface
Preface
This guide describes the updated language features in Java SE 9 and subsequent releases.
Audience
This document is for Java developers.
Documentation Accessibility
For information about Oracle's commitment to accessibility, visit the Oracle Accessibility
Program website at [Link]
Related Documents
See JDK 21 Documentation.
Conventions
The following text conventions are used in this document:
Convention Meaning
boldface Boldface type indicates graphical user interface elements associated with an
action, or terms defined in text or the glossary.
italic Italic type indicates book titles, emphasis, or placeholder variables for which
you supply particular values.
vi
Preface
Convention Meaning
monospace Monospace type indicates commands within a paragraph, URLs, code in
examples, text that appears on the screen, or text that you enter.
vii
1
Java Language Changes Summary
The following tables summarize new Java language features since Java SE 9.
1-1
Chapter 1
• : Permanent feature
Feature 21 20 19 18 17
Simple Source Files and Instance Main Methods
JEP
445
1-2
Chapter 1
Feature 21 20 19 18 17
Unnamed Patterns and Variables
JEP
443
String Templates
JEP
430
Record Patterns
JEP JEP JEP
440 432 405
Pattern Matching for switch Expressions and Statements
JEP JEP JEP JEP JEP
441 433 427 420 406
Sealed Classes
JEP
409
Feature 16 15 14 13 12 11 10 9
Sealed Classes
JEP JEP
397 360
Record Classes
JEP JEP JEP
395 384 359
Pattern Matching for the instanceof Operator
JEP JEP JEP
394 375 305
Text Blocks
JEP JEP JEP
378 368 355
Switch Expressions
JEP JEP JEP
361 354 325
Local-Variable Syntax for Lambda Parameters: see Local Variable Type
Inference JEP
323
Local Variable Type Inference
JEP
286
Java Platform Module System: see Project Jigsaw on OpenJDK
JSR
376
Milling Project Coin: see Java Language Changes for Java SE 9
JEP
213
Small Enhancements to the Java Programming Language: see Java
Language Changes for Java SE 9 JSR
334
1-3
2
Java Language Changes
This section summarizes the updated language features in Java SE 9 and subsequent
releases.
2-1
Chapter 2
Java Language Changes for Java SE 20
2-2
Chapter 2
Java Language Changes for Java SE 18
2-3
Chapter 2
Java Language Changes for Java SE 17
2-4
Chapter 2
Java Language Changes for Java SE 16
2-5
Chapter 2
Java Language Changes for Java SE 15
2-6
Chapter 2
Java Language Changes for Java SE 13
2-7
Chapter 2
Java Language Changes for Java SE 10
// A final resource
final Resource resource1 = new Resource("resource1");
// An effectively final resource
Resource resource2 = new Resource("resource2");
2-8
Chapter 2
Java Language Changes for Java SE 9
There is a more complete description of the try-with-resources statement in The Java Tutorials
(Java SE 8 and earlier).
2-9
3
Preview Features
A preview feature is a new feature whose design, specification, and implementation are
complete, but which is not permanent, which means that the feature may exist in a different
form or not at all in future JDK releases.
Introducing a feature as a preview feature in a mainline JDK release enables the largest
developer audience possible to try the feature out in the real world and provide feedback. In
addition, tool vendors are encouraged to build support for the feature before Java developers
use it in production. Developer feedback helps determine whether the feature has any design
mistakes, which includes hard technical errors (such as a flaw in the type system), soft
usability problems (such as a surprising interaction with an older feature), or poor architectural
choices (such as one that forecloses on directions for future features). Through this feedback,
the feature's strengths and weaknesses are evaluated to determine if the feature has a long-
term role in the Java SE Platform, and if so, whether it needs refinement. Consequently, the
feature may be granted final and permanent status (with or without refinements), or undergo a
further preview period (with or without refinements), or else be removed.
Every preview feature is described by a JDK Enhancement Proposal (JEP) that defines its
scope and sketches its design. For example, JEP 325 describes the JDK 12 preview feature
for switch expressions. For background information about the role and lifecycle of preview
features, see JEP 12.
For example, suppose you have an application named [Link] that uses the JDK 12
preview language feature switch expressions. Compile this with JDK 12 as follows:
Note:
When you compile an application that uses preview features, you'll receive a warning
message similar to the following:
Remember that preview features are subject to change and are intended to provoke
feedback.
3-1
Chapter 3
To run an application that uses preview features from JDK release n, use java from JDK
release n with the --enable-preview option. To continue the previous example, to run MyApp,
run java from JDK 12 as follows:
Note:
Code that uses preview features from an older release of the Java SE Platform will
not necessarily compile or run on a newer release.
The tools jshell and javadoc also support the --enable-preview command-line option.
Sending Feedback
You can provide feedback on preview features, or anything else about the Java SE Platform,
as follows:
• If you find any bugs, then submit them at Java Bug Database.
• If you want to provide substantive feedback on the usability of a preview feature, then post
it on the OpenJDK mailing list where the feature is being discussed. To find the mailing list
of a particular feature, see the feature's JEP page and look for the label Discussion. For
example, on the page JEP 325: Switch Expressions (Preview), you'll find "Discussion
amber dash dev at openjdk dot java dot net" near the top of the page.
• If you are working on an open source project, then see Quality Outreach on the OpenJDK
Wiki.
3-2
4
String Templates
String templates complement Java's existing string literals and text blocks by coupling literal
text with embedded expressions and template processors to produce specialized results. An
embedded expression is a Java expression except it has additional syntax to differentiate it
from the literal text in the string template. A template processor combines the literal text in the
template with the values of the embedded expressions to produce a result.
See the StringTemplate interface in the Java SE API specification for more information.
Note:
This is a preview feature. A preview feature is a feature whose design, specification,
and implementation are complete, but is not permanent. A preview feature may exist
in a different form or not at all in future Java SE releases. To compile and run code
that contains preview features, you must specify additional command-line options.
See Preview Language and VM Features.
For background information about string templates, see JEP 430.
My name is Duke
The template processor STR is one of the template processors that's included in the JDK. It
automatically performs string interpolation by replacing each embedded expression in the
template with its value, converted to a string. The JDK includes two other template processors:
• The FMT Template Processor: It's like the STR template processor except that it accepts
format specifiers as defined in [Link] and locale information in a similar
way as in printf method invocations.
• The RAW Template Processor: It doesn't automatically process the string template like the
STR template processors. You can use it to help you create your own template processors.
Note that you can also implement the [Link] interface to create
a template processor. See Creating a Template Processor.
4-1
Chapter 4
Embedded Expressions in String Templates
A dot (.) follows the template processor, which makes template expressions look similar to
accessing an object's field or calling an object's method.
A string template follows the dot character, which is a string that contains one or more
embedded expressions. An embedded expression is a Java expression that's surrounded by a
backslash and opening brace (\{) and a closing brace }. See Embedded Expressions in String
Templates for additional examples.
As with any Java expression, embedded expressions are evaluated from left to right. They can
also contain prefix and postfix operators.
int index = 0;
String data = STR."\{index++}, \{index++}, \{++index}, \{index++}, \{index}";
[Link](data);
0, 1, 3, 3, 4
4-2
Chapter 4
Embedded Expressions in String Templates
Locale.CANADA_FRENCH.getLanguage()}";
[Link](canLang);
Today is 2023-07-11
The language code of fr_CA is fr
Note that you can insert line breaks in an embedded expression and not introduce newlines in
the result like in the previous example. In addition, you don't have to escape quotation marks in
an embedded expression.
X, Y, Z
To make this example clearer, you can substitute the embedded template expression with a
variable:
4-3
Chapter 4
Multiline String Templates
<html>
<head>
<title>My Web Page</title>
</head>
<body>
<p>Hello, world</p>
</body>
</html>
{
"name": "Java Duke",
"phone": "555-123-4567",
"address": "1 Maple Drive, Anytown"
}
4-4
Chapter 4
The FMT Template Processor
You can use a text block to work with tabular data more clearly.
[Link](table);
The following example is just like the tabular data example in Multiline String Templates but
better formatted.
[Link](formattedTable);
4-5
Chapter 4
The RAW Template Processor
[Link]([Link]());
[Link]([Link](STR));
[Link]([Link](rawST));
[Link]([Link]());
StringTemplate{ fragments = [ "", " plus ", " equals ", "" ], values = [10,
20, 30] }
[][ plus ][ equals ][]
[10][20][30]
10 plus 20 equals 30
10 plus 20 equals 30
10 plus 20 equals 30
The method StringTemplate::fragments returns a list of fragment literals, which are the
character sequences preceding each of the embedded expressions, plus the character
sequence following the last embedded expression. In this example, the first and last fragment
literals are zero-length strings because an embedded expression appears at the beginning and
end of the template.
The method StringTemplate::values returns a list of embedded expression results.
These values are computed every time a string template is evaluated. However, fragment
4-6
Chapter 4
Creating a Template Processor
literals are constant across all evaluations of a template expression. The following example
demonstrates this.
int t = 20;
for (int u = 0; u < 3; u++) {
StringTemplate stLoop = [Link]."\{t} plus \{u} equals \{t +
u}";
[Link]("Fragments: " + [Link]());
[Link]("Values: " + [Link]());
}
Note:
These examples use the classes Json, JsonException, JsonObject, and
JsonReader from the [Link] package, which contains the Jakarta JSON
Processing API. It also indirectly uses the
[Link] class from Eclipse Parsson, which
is an implementation of Jakarta JSON Processing Specification. Obtain libraries for
these APIs from Eclipse GlassFish.
4-7
Chapter 4
Creating a Template Processor
String pw = "my_password";
[Link](newAccount);
newAccount = JSON."""
{
"account": "\{accountType}",
"user": "\{userName}",
"password": "\{pw}"
}
""";
[Link](newAccount);
{"account":"user","user":"Duke","password":"my_password"}
{"account":"administrator","user":"Duke","password":"my_password"}
There's a problem with this example: It is susceptible to one type of JSON injection attack. This
type of attack involves inserting malicious data containing quotation marks in a JSON string,
changing the JSON string itself. In this example, it changes the user name to "Duke\",
\"account\": \"administrator". The resulting JSON string becomes the following:
{
"account": "user",
"user": "Duke",
"account": "administrator",
"password": "my_password"
}
If the JSON parser encounters entries with the same name, it takes the last one. Consequently,
the user Duke now has administrator privileges.
The following example addresses this kind of JSON injection attack. It throws an exception if
the template contains any strings with a quotation mark. It also throws an exception if any of its
values aren't numbers, or Boolean values.
4-8
Chapter 4
Creating a Template Processor
if ([Link](invalidStrings).anyMatch(str::contains)) {
throw new JsonException("Injection vulnerability");
}
[Link](str);
} else if (value instanceof Number ||
value instanceof Boolean) {
[Link](value);
} else {
throw new JsonException("Invalid value type");
}
}
String jsonSource =
[Link]([Link](), filtered);
try {
JsonObject newAccount = JSON_VALIDATE."""
{
"account": "\{accountType}",
"user": "\{userName}",
"password": "\{pw}"
}
""";
[Link](newAccount);
newAccount = JSON_VALIDATE."""
{
"account": "\{accountType}",
"user": "\{userName}",
"password": "\{pw}"
}
""";
[Link](newAccount);
4-9
Chapter 4
Creating a Template Processor
{"account":"user","user":"Duke","password":"my_password"}
Injection vulnerability
Note:
You can add this method to [Link].
4-10
Chapter 4
Creating a Template Processor
There's a problem with this example: It is susceptible to SQL injection attacks. Suppose you
call the method as follows:
[Link](
myConnection, "Superior Coffee' OR s.SUP_NAME <> 'Superior Coffee");
The Statement object treats the "invalid" supplier name as part of an SQL statement. As a
result, the method prints all entries in the SUPPLIERS table, potentially exposing confidential
information.
Prepared statements help prevent SQL injection attacks. They always treat client-supplied data
as content of a parameter and never as a part of an SQL statement. The following example
creates an SQL query string from a string template, creates a JDBC PreparedStatement
from the query string, and then sets its parameters to the embedded expressions' values.
4-11
Chapter 4
Creating a Template Processor
The following example is like the getSupplierInfo example except that it uses the
QueryBuilder template processor:
4-12
Chapter 4
Creating a Template Processor
# resources_en_US.properties file
_.[Link]._=\
{0} chose option {1}
# resources_fr_CA.properties file
_.[Link]._=\
{0} a choisi l''option {1}
The following example uses LocalizationProcessor and these two resource bundles:
4-13
5
Unnamed Classes and Instance Main
Methods
The addition of instance main methods and unnamed classes to the Java language enables
students to write streamlined declarations for single-class programs and then seamlessly
expand their programs later to use more advanced features as their skills grow.
Two preview features, instance main methods and unnamed classes, are added to the Java
language. This is an evolutionary step in the language that enables students to begin writing
small programs without needing to understand the full set of language features designed for
large programs. Far from being a separate dialect, students can now use the Java language to
write streamlined declarations for single-class programs and later seamlessly expand their
beginning programs to use more advanced features as their skills grow. Java veterans might
also find instance main methods and unnamed classes features useful when writing simple
Java programs that do not require programming-in-the-large scaffolding of the Java language.
Note:
This is a preview feature. A preview feature is a feature whose design, specification,
and implementation are complete, but is not permanent. A preview feature may exist
in a different form or not at all in future Java SE releases. To compile and run code
that contains preview features, you must specify additional command-line options.
See Preview Language and VM Features.
For background information about instance main methods and unnamed classes, see
JEP 445.
The Java language enables development teams to create, develop, and maintain large and
complex applications over many years. It is a multiparadigm language with rich features for
data hiding, reuse, access control, namespace management, and modularity which allow
components to be cleanly composed while being developed and maintained independently.
With these features, components can expose well-defined interfaces for interaction with other
components and also hide internal implementation details to permit the independent evolution
of each. The object-oriented paradigm itself is designed for plugging together pieces that
interact through well-defined protocols and also to abstract away implementation details. This
composition of large components is called "programming in the large."
The Java language also offers many constructs useful for "programming in the small" in which
everything is internal to a component. In recent years, it has enhanced its programming-in-the-
large capabilities with modules and its programming-in-the-small capabilities with data-oriented
programming.
The Java language is also intended to be a first programming language. When programmers
first start out they do not write large programs, as part of a development team. They write small
programs, alone. They have no need for encapsulation and namespaces which are useful to
separately evolve components written by different people. When teaching programming,
instructors start with the basic programming-in-the-small concepts of variables, control flow,
5-1
Chapter 5
Flexible Launch Protocol
and subroutines. At that stage there is no need for the programming-in-the-large concepts of
classes, packages, and modules.
Consider the classic Hello, World! program that is often used as the first program for Java
students:
By allowing instance main methods, the new preview language features enhance the Java
launch protocol and provide greater flexibility in the declaration of a program's entry point as
follows:
5-2
Chapter 5
Selecting a main Method
• Allows the main method of a launched class to have public, protected, or default (such
as package) access.
• If a launched class does not contain a static main method with a String[] parameter,
but does contain a static main method without parameters, the launch protocol invokes
that method.
• If a launched class does not contain a static main method, but has a non-private zero-
parameter constructor (such as, public, protected, or package access) and a non-
private instance main method, the launch protocol constructs an instance of the class. If
the class has an instance main method with a String[] parameter, the launch protocol
invokes that method; otherwise, it invokes the instance main method with no parameters.
By using instance main methods, we can simplify the Hello, World! program presented in
Unnamed Classes and Instance Main Methods to:
class HelloWorld {
void main() {
[Link]("Hello, World!");
}
}
Note:
If the launched class declares an instance main method, that method will be invoked
rather than an inherited "traditional" public static void main(String[] args)
declared in a superclass. Therefore, if the launched class inherits a "traditional" main
method but another method (such as, an instance main) is selected, the JVM will
issue a warning to the standard error at runtime.
If the selected main is an instance method and is a member of an inner class, the program will
fail to launch.
5-3
Chapter 5
Unnamed Classes
Unnamed Classes
In the Java language, every class resides in a package and every package resides in a
module. These namespacing and encapsulation constructs apply to all code; however, small
programs that do not need them can omit them.
A program that does not need class namespaces can omit the package statement, making its
classes implicit members of the unnamed package. Classes in the unnamed package cannot
be referenced explicitly by classes in named packages. A program that does not need to
encapsulate its packages can omit the module declaration, making its packages implicit
members of the unnamed module. Packages in the unnamed module cannot be referenced
explicitly by packages in named modules.
Before classes serve their main purpose as templates for the construction of objects, they
serve as namespaces for methods and fields. We should not require students to confront the
concept of classes:
• Before they are comfortable with the basic building blocks of variables, control flow, and
subroutines,
• Before they embark on learning object orientation, and
• When they are still writing simple, single-file programs.
Even though every method resides in a class, we can stop requiring explicit class declarations
for code that does not need it — just as we do not require explicit package or module
declarations for code that does not need them.
When the Java compiler encounters a source file containing a method that is not enclosed in a
class declaration, it implicitly considers such methods (as well as any unenclosed fields and
any classes declared in the file) to be members of an unnamed top-level class.
An unnamed class is always a member of the unnamed package. It is also final and cannot
implement any interface or extend any class other than Object. An unnamed class cannot be
referenced by name, so there can be no method references to its static methods. However, the
this keyword can still be used, as well as method references to instance methods.
No code can refer to an unnamed class by name, so instances of an unnamed class cannot be
constructed directly. Such a class is useful only as a standalone program or as an entry point
to a program. Therefore, an unnamed class must have a main method that can be launched as
described above. This requirement is enforced by the Java compiler.
An unnamed class resides in the unnamed package, and the unnamed package resides in the
unnamed module. While there can only be one unnamed package (barring multiple class
loaders) and only one unnamed module, there can be multiple unnamed classes in the
unnamed module. Every unnamed class contains a main method and represents a program.
Multiple such classes in the unnamed package represent multiple programs.
An unnamed class is almost exactly like an explicitly declared class. Its members can have the
same modifiers (such as private and static) and the modifiers have the same defaults (such
as package access and instance membership). The class can have static initializers as well as
instance initializers. One key difference is that while an unnamed class has a default zero-
parameter constructor, it can have no other constructor.
5-4
Chapter 5
Growing a Program
void main() {
[Link]("Hello, World!");
}
Because top-level members are interpreted as members of the unnamed class, we can also
write the program as:
void main() {
[Link](greeting());
}
void main() {
[Link](greeting);
}
A source file named [Link] that contains an unnamed class can be launched with
the source-code launcher as follows:
$ java [Link]
The Java compiler compiles that file to the launchable class file [Link]. In this
case, the compiler chooses HelloWorld for the class name as an implementation detail.
However, that name still cannot be used directly in Java source code.
At this time, the javadoc tool will fail when asked to generate API documentation for a Java file
with an unnamed class because unnamed classes do not define an API that is accessible from
other classes. This behavior may change in a future release.
The [Link] method returns true for an unnamed class.
Growing a Program
A Hello, World! program written as an unnamed class is more focused on what the program
actually does by omitting concepts and constructs it does not need. Even so, all members are
interpreted just as they are in an ordinary class.
Concepts and constructs can be added to an unnamed class as as needed by the program. An
unnamed class can easily be evolved later into an ordinary class, by wrapping its declaration
(excluding import statements) inside an explicit class declaration.
Eliminating the main method altogether might seem like a natural next step, but it would work
against the goal of gracefully evolving a student's first Java program into a larger one as well
as impose some non-obvious restrictions (see see the section Alternatives in JEP 445).
Dropping the void modifier would similarly create a distinct Java dialect.
5-5
6
Sealed Classes
Sealed classes and interfaces restrict which other classes or interfaces may extend or
implement them.
For background information about sealed classes and interfaces, see JEP 409.
One of the primary purposes of inheritance is code reuse: When you want to create a new
class and there is already a class that includes some of the code that you want, you can derive
your new class from the existing class. In doing this, you can reuse the fields and methods of
the existing class without having to write (and debug) them yourself.
However, what if you want to model the various possibilities that exist in a domain by defining
its entities and determining how these entities should relate to each other? For example, you're
working on a graphics library. You want to determine how your library should handle common
geometric primitives like circles and squares. You've created a Shape class that these
geometric primitives can extend. However, you're not interested in allowing any arbitrary class
to extend Shape; you don't want clients of your library declaring any further primitives. By
sealing a class, you can specify which classes are permitted to extend it and prevent any other
arbitrary class from doing so.
Define the following three permitted subclasses, Circle, Square, and Rectangle, in the same
module or in the same package as the sealed class:
6-1
Chapter 6
Alternatively, you can define permitted subclasses in the same file as the sealed class. If you
do so, then you can omit the permits clause:
package [Link];
6-2
Chapter 6
package [Link];
package [Link];
6-3
Chapter 6
package [Link];
6-4
Chapter 6
The cast expression Polygon p = (Polygon) r is allowed because it's possible that the
Rectangle value r could be of type Polygon; Rectangle is a subtype of Polygon. However,
consider this example:
Even though the class Triangle and the interface Polygon are unrelated, the cast expression
Polygon p = (Polygon) t is also allowed because at run time these types could be related. A
developer could declare the following class:
6-5
Chapter 6
However, there are cases where the compiler can deduce that there are no values (other than
the null reference) shared between two types; these types are considered disjoint. For
example:
Because the class UtahTeapot is final, it's impossible for a class to be a descendant of both
Polygon and UtahTeapot. Therefore, Polygon and UtahTeapot are disjoint, and the cast
statement Polygon p = (Polygon) u isn't allowed.
The compiler has been enhanced to navigate any sealed hierarchy to check if your cast
statements are allowed. For example:
The first cast statement UtahTeapot u = (UtahTeapot) s isn't allowed; a Shape can only be a
Polygon because Shape is sealed. However, as Polygon is non-sealed, it can be extended.
However, no potential subtype of Polygon can extend UtahTeapot as UtahTeapot is final.
Therefore, it's impossible for a Shape to be a UtahTeapot.
In contrast, the second cast statement Ring r = (Ring) s is allowed; it's possible for a Shape
to be a Ring because Ring is not a final class.
6-6
7
Pattern Matching
Pattern matching involves testing whether an object has a particular structure, then extracting
data from that object if there's a match. You can already do this with Java. However, pattern
matching introduces new language enhancements that enable you to conditionally extract data
from objects with code that's more concise and robust.
Topics
• Pattern Matching for the instanceof Operator
• Pattern Matching for switch Expressions and Statements
• Record Patterns
• Unnamed Patterns and Variables
Consider the following code that calculates the perimeter of certain shapes:
7-1
Chapter 7
Pattern Matching for the instanceof Operator
Note:
Removing this conversion step also makes your code safer. Testing an object's type
with the instanceof, then assigning that object to a new variable with a cast can
introduce coding errors in your application. You might change the type of one of the
objects (either the tested object or the new variable) and accidentally forget to
change the type of the other object.
A pattern is a combination of a test, which is called a predicate; a target; and a set of local
variables, which are called pattern variables. The getPerimeter example contains two
patterns, s instanceof Rectangle r and s instanceof Circle c:
• The predicate is a Boolean-valued function with one argument. In these two patterns, it’s
the instanceof operator, testing if the s argument is a Rectangle or a Circle.
• The target is the argument of the predicate. In these two patterns, it's the s value.
• The pattern variables are those that store data from the target only if the predicate returns
true. In these two patterns, they're the variables r and c.
A type pattern consists of a predicate that specifies a type, along with a single pattern variable.
In this example, the type patterns are Rectangle r and Circle c.
7-2
Chapter 7
Pattern Matching for the instanceof Operator
The scope of a pattern variable can extend beyond the statement that introduced it:
Because the conditional-AND operator (&&) is short-circuiting, the program can reach the
[Link]() > 5 expression only if the instanceof operator is true.
Conversely, you can't pattern match with the instanceof operator in this situation:
The program can reach the [Link]() || 5 if the instanceof is false; thus, you cannot use
the pattern variable r here.
See Scope of Pattern Variable Declarations for more examples of where you can use a pattern
variable.
7-3
Chapter 7
Pattern Matching for switch Expressions and Statements
interface Shape { }
record Rectangle(double length, double width) implements Shape { }
record Circle(double radius) implements Shape { }
...
public static double getPerimeter(Shape s) throws
IllegalArgumentException {
if (s instanceof Rectangle r) {
return 2 * [Link]() + 2 * [Link]();
} else if (s instanceof Circle c) {
return 2 * [Link]() * [Link];
} else {
throw new IllegalArgumentException("Unrecognized shape");
}
}
You can rewrite this code to use a pattern switch expression as follows:
7-4
Chapter 7
Pattern Matching for switch Expressions and Statements
}
}
Topics
• Selector Expression Type
• When Clauses
• Qualified enum Constants as case Constants
• Pattern Label Dominance
• Type Coverage in switch Expressions and Statements
• Inference of Type Arguments in Record Patterns
• Scope of Pattern Variable Declarations
• Null case Labels
When Clauses
You can add a Boolean expression right after a pattern label with a when clause. This is called
a guarded pattern label. The Boolean expression in the when clause is called a guard. A value
matches a guarded pattern label if it matches the pattern and, if so, the guard also evaluates to
true. Consider the following example:
7-5
Chapter 7
Pattern Matching for switch Expressions and Statements
} else {
[Link](s);
}
break;
default:
[Link]("Not a string");
}
}
You can move the Boolean expression [Link] == 1 right after the the case label with a when
clause:
The first pattern label (which is a guarded pattern label) matches if obj is both a String and of
length 1. The second patten label matches if obj is a String of a different length.
A guarded patten label has the form p when e where p is a pattern and e is a Boolean
expression. The scope of any pattern variable declared in p includes e.
Consider the following switch expression whose selector expression is an enum type:
In the following example, the type of the selector expression is an interface that's been
implemented by two enum types. Because the type of the selector expression isn't an enum
type, this switch expression uses guarded patterns instead:
7-6
Chapter 7
Pattern Matching for switch Expressions and Statements
However, switch expressions and statements allow qualified enum constants, so you could
rewrite this example as follows:
Therefore, you can use an enum constant when the type of the selector expression is not an
enum type provided that the enum constant's name is qualified and its value is assignment-
compatible with the type of the selector expression.
7-7
Chapter 7
Pattern Matching for switch Expressions and Statements
The first pattern label case CharSequence cs dominates the second pattern label case String
s because every value that matches the pattern String s also matches the pattern
CharSequence cs but not the other way around. It's because String is a subtype of
CharSequence.
A pattern label can dominate a constant label. These examples cause compile-time errors:
7-8
Chapter 7
Pattern Matching for switch Expressions and Statements
Note:
Guarded pattern labels don't dominate constant labels. For example:
Although the value 1 matches both the guarded pattern label case Integer i when
i > 0 and the constant label case 1, the guarded pattern label doesn't dominate the
constant label. Guarded patterns aren't checked for dominance because they're
generally undecidable. Consequently, you should order your case labels so that
constant labels appear first, followed by guarded pattern labels, and then followed by
nonguarded pattern labels:
7-9
Chapter 7
Pattern Matching for switch Expressions and Statements
};
}
However, the type coverage of the case label default is all types, so the following example
compiles:
The compiler takes into account whether the type of a selector expression is a sealed class.
The following switch expression compiles. It doesn't need a default case label because its
type coverage is the classes A, B, and C, which are the only permitted subclasses of S, the type
of the selector expression:
The compiler can also determine the type coverage of a switch expression or statement if the
type of its selector expression is a generic sealed class. The following example compiles. The
only permitted subclasses of interface I are classes A and B. However, because the selector
expression is of type I<Integer>, the switch block requires only class B in its type coverage to
be exhaustive:
7-10
Chapter 7
Pattern Matching for switch Expressions and Statements
The type of a switch expression or statement's selector expression can also be a generic
record. As always, a switch expression or statement must be exhaustive. The following
example doesn't compile. No match for a Pair exists that contains two values, both of type A:
record Pair<T>(T x, T y) {}
class A {}
class B extends A {}
The following example compiles. Interface I is sealed. Types C and D cover all possible
instances:
record Pair<T>(T x, T y) {}
sealed interface I permits C, D {}
record C(String s) implements I {}
record D(String s) implements I {}
If a switch expression or statement is exhaustive at compile time but not at run time, then a
MatchException is thrown. This can happen when a class that contains an exhaustive switch
expression or statement has been compiled, but a sealed hierarchy that is used in the analysis
of the switch expression or statement has been subsequently changed and recompiled. Such
changes are migration incompatible and may lead to a MatchException being thrown when
running the switch statement or expression. Consequently, you need to recompile the class
containing the switch expression or statement.
class ME {
public static void main(String[] args) {
[Link](switch ([Link]()) {
case A a -> 1;
case B b -> 2;
});
}
}
7-11
Chapter 7
Pattern Matching for switch Expressions and Statements
The switch expression in the class ME is exhaustive and this example compiles. When you run
ME, it prints the value 1. However, suppose you edit Seal as follows and compile this class and
not ME:
See Record Patterns for more examples of inference of type arguments in record patterns.
7-12
Chapter 7
Pattern Matching for switch Expressions and Statements
In a switch expression or statement, the scope of a pattern variable declared in a case label
includes the following:
• The when clause of the case label:
The scope of pattern variable c includes the when clause of the case label that contains the
declaration of c.
• The expression, block, or throw statement that appears to the right of the arrow of the case
label:
The scope of pattern variable c includes the block to the right of case Character c ->.
The scope of pattern variable i includes the println statement to the right of case
Integer i ->.
7-13
Chapter 7
Pattern Matching for switch Expressions and Statements
The scope of pattern variable c includes the case Character c statement group: the two
if statements and the println statement that follows them. The scope doesn't include the
default statement group even though the switch statement can execute the case
Character c statement group, fall through the default label, and then execute the
default statement group.
Note:
You will get a compile-time error if it's possible to fall through a case label that
declares a pattern variable. The following example doesn't compile:
If this code were allowed, and the value of the selector expression, obj, were a Character,
then the switch statement can execute the case Character c statement group and then
fall through the case Integer i label, where the pattern variable i would have not been
initialized.
7-14
Chapter 7
Pattern Matching for switch Expressions and Statements
This example prints null! when obj is null instead of throwing a NullPointerException.
You may not combine a null case label with anything but a default case label. The following
generates a compiler error:
If a selector expression evaluates to null and the switch block does not have null case label,
then a NullPointerException is thrown as normal. Consider the following switch statement:
String s = null;
switch (s) {
case Object obj -> [Link]("This doesn't match null");
// No null label; NullPointerException is thrown
// if s is null
}
Although the pattern label case Object obj matches objects of type String, this example
throws a NullPointerException. The selector expression evaluates to null, and the switch
expression doesn't contain a null case label.
7-15
Chapter 7
Record Patterns
Record Patterns
You can use a record pattern to test whether a value is an instance of a record class type (see
Record Classes) and, if it is, to recursively perform pattern matching on its component values.
For background information about record patterns, see JEP 440.
The following example tests whether obj is an instance of the Point record with the record
pattern Point(double x, double y):
In addition, this example extracts the x and y values from obj directly, automatically calling the
Point record's accessor methods.
A record pattern consists of a type and a (possibly empty) record pattern list. In this example,
the type is Point and the pattern list is (double x, double y).
Note:
The null value does not match any record pattern.
The following example is the same as the previous one except it uses a type pattern instead of
a record pattern:
Generic Records
If a record class is generic, then you can explicitly specify the type arguments in a record
pattern. For example:
record Box<T>(T t) { }
7-16
Chapter 7
Record Patterns
You can test whether a value is an instance of a parameterized record type provided that the
value could be cast to the record type in the pattern without requiring an unchecked
conversion. The following example doesn't compile:
Type Inference
You can use var in the record pattern's component list. In the following example, the compiler
infers that the pattern variables x and y are of type double:
The compiler can infer the type of the type arguments for record patterns in all constructs that
accept patterns: switch statements, switch expressions, and instanceof expressions.
The following example is equivalent to printBoxContents. The compiler infers its type
argument and pattern variable: Box(var s) is inferred as Box<String>(String s)
7-17
Chapter 7
Unnamed Patterns and Variables
You can do the same for parameterized records. The compiler infers the types of the record
pattern's type arguments and pattern variables. In the following example, the compiler infers
Box(Box(var s)) as Box<Box<String>>(Box(String s)).
Note:
This is a preview feature. A preview feature is a feature whose design, specification,
and implementation are complete, but is not permanent. A preview feature may exist
in a different form or not at all in future Java SE releases. To compile and run code
that contains preview features, you must specify additional command-line options.
See Preview Language and VM Features.
For background information about unnamed patterns and variables, see JEP 443.
Unnamed Patterns
Consider the following example that calculates the distance between two instances of
ColoredPoint:
7-18
Chapter 7
Unnamed Patterns and Variables
The example doesn't use the Color component of the ColoredPoint record. To simplify the
code and improve readability, you can omit or elide the type patterns Color c1 and Color c2
with the unnamed pattern (_):
Alternatively, you can keep the type pattern's type and elide just its name:
No value is bound to the unnamed pattern variable. Consequently, the highlighted statement in
the following example is invalid:
void printSalary(Employee b) {
switch (b) {
case Salaried r -> [Link]("Salary: " + [Link]());
case Freelancer _ -> [Link]("Other");
7-19
Chapter 7
Unnamed Patterns and Variables
You may use multiple patterns in a case label provided that they don't declare any pattern
variables. For example, you can rewrite the previous switch statement as follows:
switch (b) {
case Salaried r -> [Link]("Salary: " +
[Link]());
case Freelancer _, Intern _ -> [Link]("Other");
}
Unnamed Variables
You can use the underscore keyword (_) not just as a pattern in a pattern list, but also as the
name of a local variable, exception, or lambda parameter in a declaration when the value of
the declaration isn't needed. This is called an unnamed variable, which represents a variable
that'ss being declared but it has no usable name.
Unnamed variables are useful when the side effect of a statement is more important than its
result.
Consider the following example that iterates through the elements of the array orderIDs with a
for loop. The side effect of this for loop is that it calculates the number of elements in
orderIDs without ever using the loop variable id:
You can use an unnamed variable to elide the unused variable id:
The following table describes where you can declare an unnamed variable:
7-20
Chapter 7
Unnamed Patterns and Variables
for (int i = 0, _ =
[Link]("Starting for-loop");
i < 10; i++) {
[Link](i);
}
7-21
Chapter 7
Unnamed Patterns and Variables
7-22
8
Record Classes
Record classes, which are a special kind of class, help to model plain data aggregates with
less ceremony than normal classes.
For background information about record classes, see JEP 395.
A record declaration specifies in a header a description of its contents; the appropriate
accessors, constructor, equals, hashCode, and toString methods are created automatically. A
record's fields are final because the class is intended to serve as a simple "data carrier".
For example, here is a record class with two fields:
A record class declaration consists of a name; optional type parameters (generic record
declarations are supported); a header, which lists the "components" of the record; and a body.
A record class declares the following members automatically:
• For each component in the header, the following two members:
– A private final field with the same name and declared type as the record
component. This field is sometimes referred to as a component field.
8-1
Chapter 8
– A public accessor method with the same name and type of the component; in the
Rectangle record class example, these methods are Rectangle::length() and
Rectangle::width().
• A canonical constructor whose signature is the same as the header. This constructor
assigns each argument from the new expression that instantiates the record class to the
corresponding component field.
• Implementations of the equals and hashCode methods, which specify that two record
classes are equal if they are of the same type and contain equal component values.
• An implementation of the toString method that includes the string representation of all the
record class's components, with their names.
As record classes are just special kinds of classes, you create a record object (an instance of a
record class) with the new keyword, for example:
Repeating the record class's components in the signature of the canonical constructor can be
tiresome and error-prone. To avoid this, you can declare a compact constructor whose
signature is implicit (derived from the components automatically).
For example, the following compact constructor declaration validates length and width in the
same way as in the previous example:
8-2
Chapter 8
This succinct form of constructor declaration is only available in a record class. Note that the
statements [Link] = length; and [Link] = width; which appear in the canonical
constructor do not appear in the compact constructor. At the end of a compact constructor, its
implicit formal parameters are assigned to the record class's private fields corresponding to its
components.
If you implement your own accessor methods, then ensure that they have the same
characteristics as implicitly derived accessors (for example, they're declared public and have
the same return type as the corresponding record class component). Similarly, if you
implement your own versions of the equals, hashCode, and toString methods, then ensure
that they have the same characteristics and behavior as those in the [Link] class,
which is the common superclass of all record classes.
You can declare static fields, static initializers, and static methods in a record class, and they
behave as they would in a normal class, for example:
// Static field
static double goldenRatio;
// Static initializer
static {
goldenRatio = (1 + [Link](5)) / 2;
}
// Static method
public static Rectangle createGoldenRectangle(double width) {
return new Rectangle(width, width * goldenRatio);
}
}
You cannot declare instance variables (non-static fields) or instance initializers in a record
class.
8-3
Chapter 8
You can declare instance methods in a record class, independent of whether you implement
your own accessor methods. You can also declare nested classes and interfaces in a record
class, including nested record classes (which are implicitly static). For example:
• You can declare a record class that implements one or more interfaces, for example:
8-4
Chapter 8
• You can annotate a record class and its individual components, for example:
import [Link].*;
@Retention([Link])
@Target([Link])
public @interface GreaterThanZero { }
record Rectangle(
@GreaterThanZero double length,
@GreaterThanZero double width) { }
If you annotate a record component, then the annotation may be propagated to members
and constructors of the record class. This propagation is determined by the contexts in
which the annotation interface is applicable. In the previous example, the
@Target([Link]) meta-annotation means that the @GreaterThanZero
annotation is propagated to the field corresponding to the record component.
Consequently, this record class declaration would be equivalent to the following normal
class declaration:
import [Link].*;
import [Link].*;
import [Link].*;
8-5
Chapter 8
List<Merchant> findTopMerchants(
List<Sale> sales, List<Merchant> merchants, int year, Month month) {
return [Link]()
.map(merchant -> new MerchantSales(
merchant, [Link](sales, merchant, year, month)))
.sorted((m1, m2) -> [Link]([Link](), [Link]()))
.map(MerchantSales::merchant)
.collect([Link]());
}
List<Merchant> topMerchants =
[Link](salesList, merchantList, 2020,
[Link]);
[Link]("Top merchants: ");
[Link]().forEach(m -> [Link]([Link]()));
}
}
8-6
Chapter 8
Like nested record classes, local record classes are implicitly static, which means that their
own methods can't access any variables of the enclosing method, unlike local classes, which
are never static.
Record Serialization
You can serialize and deserialize instances of record classes, but you can't customize the
process by providing writeObject, readObject, readObjectNoData, writeExternal,
or readExternal methods. The components of a record class govern serialization, while the
canonical constructor of a record class governs deserialization. See Serializable Records for
more information and an extended example. See also the section Serialization of Records in
the Java Object Serialization Specification.
You might get a compiler error if your source file imports a class named Record from a
package other than [Link]. A Java source file automatically imports all the types in the
8-7
Chapter 8
[Link] package though an implicit import [Link].*; statement. This includes the
[Link] class, regardless of whether preview features are enabled or disabled.
package [Link];
package [Link];
import [Link].*;
Both Record in the [Link] package and Record in the [Link] package are imported
with a wildcard. Consequently, neither class takes precedence, and the compiler generates an
error when it encounters the use of the simple name Record.
To enable this example to compile, change the import statement so that it imports the fully
qualified name of Record:
import [Link];
8-8
Chapter 8
Note:
The introduction of classes in the [Link] package is rare but necessary from
time to time, such as Enum in Java SE 5, Module in Java SE 9, and Record in Java
SE 14.
8-9
9
Switch Expressions
Like all expressions, switch expressions evaluate to a single value and can be used in
statements. They may contain "case L ->" labels that eliminate the need for break statements
to prevent fall through. You can use a yield statement to specify the value of a switch
expression.
For background information about the design of switch expressions, see JEP 361.
// ...
int numLetters = 0;
Day day = [Link];
switch (day) {
case MONDAY:
case FRIDAY:
case SUNDAY:
numLetters = 6;
break;
case TUESDAY:
numLetters = 7;
break;
case THURSDAY:
case SATURDAY:
numLetters = 8;
break;
case WEDNESDAY:
numLetters = 9;
break;
default:
throw new IllegalStateException("Invalid day: " + day);
}
[Link](numLetters);
It would be better if you could "return" the length of the day's name instead of storing it in the
variable numLetters; you can do this with a switch expression. Furthermore, it would be better
if you didn't need break statements to prevent fall through; they are laborious to write and easy
to forget. You can do this with a new kind of case label. The following is a switch expression
that uses the new kind of case label to print the number of letters of a day of the week:
9-1
Chapter 9
When the Java runtime matches any of the labels to the left of the arrow, it runs the code to the
right of the arrow and does not fall through; it does not run any other code in the switch
expression (or statement). If the code to the right of the arrow is an expression, then the value
of that expression is the value of the switch expression.
You can use the new kind of case label in switch statements. The following is like the first
example, except it uses "case L ->" labels instead of "case L:" labels:
int numLetters = 0;
Day day = [Link];
switch (day) {
case MONDAY, FRIDAY, SUNDAY -> numLetters = 6;
case TUESDAY -> numLetters = 7;
case THURSDAY, SATURDAY -> numLetters = 8;
case WEDNESDAY -> numLetters = 9;
default -> throw new IllegalStateException("Invalid day: " + day);
};
[Link](numLetters);
A "case L ->" label along with its code to its right is called a switch labeled rule.
9-2
Chapter 9
yield 9;
default:
throw new IllegalStateException("Invalid day: " + day);
};
[Link](numLetters);
The previous example uses yield statements. They take one argument, which is the value that
the case label produces in a switch expression.
The yield statement makes it easier for you to differentiate between switch statements and
switch expressions. A switch statement, but not a switch expression, can be the target of a
break statement. Conversely, a switch expression, but not a switch statement, can be the
target of a yield statement.
Note:
It's recommended that you use "case L ->" labels. It's easy to forget to insert break
or yield statements when using "case L:" labels; if you do, you might introduce
unintentional fall through in your code.
For "case L ->" labels, to specify multiple statements or code that are not
expressions or throw statements, enclose them in a block. Specify the value that the
case label produces with the yield statement:
9-3
Chapter 9
In addition, a switch expression must either complete normally with a value or complete
abruptly by throwing an exception. For example, the following code doesn't compile because
the switch labeled rule doesn't contain a yield statement:
The following example doesn't compile because the switch labeled statement group doesn't
contain a yield statement:
i = switch (day) {
case MONDAY, TUESDAY, WEDNESDAY:
yield 0;
default:
[Link]("Second half of the week");
// ERROR! Group doesn't contain a yield statement
};
Because a switch expression must evaluate to a single value (or throw an exception), you
can't jump through a switch expression with a break, yield, return, or continue statement,
like in the following example:
z:
for (int i = 0; i < MAX_VALUE; ++i) {
int k = switch (e) {
case 0:
yield 1;
case 1:
yield 2;
default:
continue z;
// ERROR! Illegal jump through a switch expression
};
// ...
}
The following example doesn't compile because the switch statement (which uses pattern
labels) is not exhaustive:
9-4
Chapter 9
[Link](s);
break;
case Integer i:
[Link]("Integer");
break;
}
}
9-5
10
Text Blocks
See Programmer's Guide to Text Blocks for more information about this language feature. For
background information about text blocks, see JEP 378.
10-1
11
Local Variable Type Inference
In JDK 10 and later, you can declare local variables with non-null initializers with the var
identifier, which can help you write code that’s easier to read.
Consider the following example, which seems redundant and is hard to read:
You can rewrite this example by declaring the local variables with the var identifier. The type of
the variables are inferred from the context:
var is a reserved type name, not a keyword, which means that existing code that uses var as a
variable, method, or package name is not affected. However, code that uses var as a class or
interface name is affected and the class or interface needs to be renamed.
var can be used for the following types of variables:
for (var counter = 0; counter < 10; counter++) {...} // infers int
• try-with-resources variable:
11-1
Chapter 11
In JDK 11 and later, you can declare each formal parameter of an implicitly typed lambda
expression with the var identifier:
11-2