Movatterモバイル変換


[0]ホーム

URL:


JEP 354: Switch Expressions (Second Preview)

AuthorGavin Bierman
OwnerJan Lahoda
TypeFeature
ScopeSE
StatusClosed / Delivered
Release13
Componentspecification / language
Discussionamber dash dev at openjdk dot java dot net
EffortS
DurationM
Relates toJEP 325: Switch Expressions (Preview)
JEP 361: Switch Expressions
Reviewed byAlex Buckley, Brian Goetz
Endorsed byBrian Goetz
Created2019/04/09 12:38
Updated2021/08/28 00:17
Issue8222184

Summary

Extendswitch so it can be used as either a statement or an expression, and so that both forms can use either traditionalcase ... : labels (with fall through) or newcase ... -> labels (with no fall through), with a further new statement for yielding a value from aswitch expression. These changes will simplify everyday coding, and prepare the way for the use ofpattern matching (JEP 305) inswitch. This is apreview language feature in JDK 13.

History

Switch expressions wereproposed in December 2017 byJEP 325. They weretargeted to JDK 12 in August 2018 as apreview feature. Feedback was sought initially on the design of the feature, and later on the experience of usingswitch expressions and the enhancedswitch statement. Based on that feedback, this JEP makes one change to the feature:

To yield a value from aswitch expression, thebreak with value statement is dropped in favor of ayield statement.

Motivation

As we prepare to enhance the Java programming language to supportpattern matching (JEP 305), several irregularities of the existingswitch statement -- which have long been an irritation to users -- become impediments. These include the default control flow behavior between switch labels (fall through), the default scoping in switch blocks (the whole block is treated as one scope), and the fact thatswitch works only as a statement, even though it is often more natural to express multi-way conditionals as expressions.

The current design of Java'sswitch statement follows closely languages such as C and C++, and supports fall through semantics by default. Whilst this traditional control flow is often useful for writing low-level code (such as parsers for binary encodings), asswitch is used in higher-level contexts, its error-prone nature starts to outweigh its flexibility. For example, in the following code, the manybreak statements make it unnecessarily verbose, and this visual noise often masks hard to debug errors, where missingbreak statements would mean accidental fall through.

switch (day) {    case MONDAY:    case FRIDAY:    case SUNDAY:        System.out.println(6);        break;    case TUESDAY:        System.out.println(7);        break;    case THURSDAY:    case SATURDAY:        System.out.println(8);        break;    case WEDNESDAY:        System.out.println(9);        break;}

We propose to introduce a new form of switch label, "case L ->", to signify that only the code to the right of the label is to be executed if the label is matched. We also propose to allow multiple constants per case, separated by commas. The previous code can now be written:

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

The code to the right of a "case L ->" switch label is restricted to be an expression, a block, or (for convenience) athrow statement. This has the pleasing consequence that should an arm introduce a local variable, it must be contained in a block and is thus not in scope for any of the other arms in the switch block. This eliminates another annoyance with traditional switch blocks where the scope of a local variable is the entire block:

switch (day) {    case MONDAY:    case TUESDAY:        int temp = ...     // The scope of 'temp' continues to the }        break;    case WEDNESDAY:    case THURSDAY:        int temp2 = ...    // Can't call this variable 'temp'        break;    default:        int temp3 = ...    // Can't call this variable 'temp'}

Many existingswitch statements are essentially simulations ofswitch expressions, where each arm either assigns to a common target variable or returns a value:

int numLetters;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("Wat: " + day);}

Expressing this as a statement is roundabout, repetitive, and error-prone. The author meant to express that we should compute a value ofnumLetters for each day. It should be possible to say that directly, using aswitchexpression, which is both clearer and safer:

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

In turn, extendingswitch to support expressions raises some additional needs, such as extending flow analysis (an expression must always compute a value or complete abruptly), and allowing some case arms of aswitch expression to throw an exception rather than yield a value.

Description

Arrow labels

In addition to traditional "case L :" labels in a switch block, we propose a new simplified form, with "case L ->" labels. If a label is matched, then only the expression or statement to the right of the arrow is executed; there is no fall through. For example, given the followingswitch statement that uses the new form of labels:

static void howMany(int k) {    switch (k) {        case 1  -> System.out.println("one");        case 2  -> System.out.println("two");        default -> System.out.println("many");    }}

The following code:

howMany(1);howMany(2);howMany(3);

results in the following output:

onetwomany

Switch expressions

We will extend theswitch statement so it can be used as an expression. For example, the previoushowMany method can be rewritten to use aswitch expression, so it uses only a singleprintln.

static void howMany(int k) {    System.out.println(        switch (k) {            case  1 -> "one"            case  2 -> "two"            default -> "many"        }    );}

In the common case, aswitch expression will look like:

T result = switch (arg) {    case L1 -> e1;    case L2 -> e2;    default -> e3;};

Aswitch expression is a poly expression; if the target type is known, this type is pushed down into each arm. The type of aswitch expression is its target type, if known; if not, a standalone type is computed by combining the types of each case arm.

Yielding a value

Mostswitch expressions will have a single expression to the right of the "case L ->" switch label. In the event that a full block is needed, we introduce a newyield statement to yield a value, which becomes the value of the enclosingswitch expression.

int j = switch (day) {    case MONDAY  -> 0;    case TUESDAY -> 1;    default      -> {        int k = day.toString().length();        int result = f(k);        yield result;    }};

Aswitch expression can, like aswitch statement, also use a traditional switch block with "case L:" switch labels (implying fall through semantics). In this case, values are yielded using the newyield statement:

int result = switch (s) {    case "Foo":         yield 1;    case "Bar":        yield 2;    default:        System.out.println("Neither Foo nor Bar, hmmm...");        yield 0;};

The two statements,break (with or without a label) andyield, facilitate easy disambiguation betweenswitch statements andswitch expressions: aswitch statement but not aswitch expression can be the target of abreak statement; and aswitch expression but not aswitch statement can be the target of ayield statement.

In the previous preview version ofswitch expressions,JEP 325, we proposed to add a new form ofbreak statement with a value, which would be used to yield a value from aswitch expression. In this version ofswitch expressions, this will be replaced with the newyield statement.

Exhaustiveness

The cases of aswitch expression must beexhaustive; for all possible values there must be a matching switch label. (Obviouslyswitch statements are not required to be exhaustive.)

In practice this normally means that adefault clause is required; however, in the case of anenumswitch expression that covers all known constants, adefault clause is inserted by the compiler to indicate that theenum definition has changed between compile-time and runtime. Relying on this implicitdefault clause insertion makes for more robust code; now when code is recompiled, the compiler checks that all cases are explicitly handled. Had the developer inserted an explicitdefault clause (as is the case today) a possible error will have been hidden.

Furthermore, aswitch expression must either complete normally with a value, or complete abruptly by throwing an exception. This has a number of consequences. First, the compiler checks that for every switch label, if it is matched then a value can be yielded.

int i = switch (day) {    case MONDAY -> {        System.out.println("Monday");         // ERROR! Block doesn't contain a yield statement    }    default -> 1;};i = switch (day) {    case MONDAY, TUESDAY, WEDNESDAY:         yield 0;    default:         System.out.println("Second half of the week");        // ERROR! Group doesn't contain a yield statement};

A further consequence is that the control statements,break,yield,return andcontinue, cannot jump through aswitch expression, such as in the following:

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         };    ...    }

Dependencies

This feature was previewed inJEP 325.

Pattern Matching (JEP 305) depends on this JEP.

Risks and Assumptions

The need for aswitch statement withcase L -> labels is sometimes unclear. The following rationale presents the assumptions behind its inclusion:

OpenJDK logo
Installing
Contributing
Sponsoring
Developers' Guide
Vulnerabilities
JDK GA/EA Builds
Mailing lists
Wiki ·IRC
Mastodon
Bluesky
Bylaws ·Census
Legal
Workshop
JEP Process
Source code
GitHub
Mercurial
Tools
Git
jtreg harness
Groups
(overview)
Adoption
Build
Client Libraries
Compatibility & Specification Review
Compiler
Conformance
Core Libraries
Governing Board
HotSpot
IDE Tooling & Support
Internationalization
JMX
Members
Networking
Porters
Quality
Security
Serviceability
Vulnerability
Web
Projects
(overview,archive)
Amber
Babylon
CRaC
Code Tools
Coin
Common VM Interface
Developers' Guide
Device I/O
Duke
Galahad
Graal
IcedTea
JDK 8 Updates
JDK 9
JDK (…,24,25,26)
JDK Updates
JMC
Jigsaw
Kona
Lanai
Leyden
Lilliput
Locale Enhancement
Loom
Memory Model Update
Metropolis
Multi-Language VM
Nashorn
New I/O
OpenJFX
Panama
Penrose
Port: AArch32
Port: AArch64
Port: BSD
Port: Haiku
Port: Mac OS X
Port: MIPS
Port: Mobile
Port: PowerPC/AIX
Port: RISC-V
Port: s390x
SCTP
Shenandoah
Skara
Sumatra
Tsan
Valhalla
Verona
VisualVM
Wakefield
Zero
ZGC
Oracle logo
© 2025 Oracle Corporation and/or its affiliates
Terms of Use · License:GPLv2 ·Privacy ·Trademarks

[8]ページ先頭

©2009-2025 Movatter.jp