JEP 361: Switch Expressions (Standard)

Switch Expressions (Standard)

Summary

확장 switch하여 명령문 또는 표현식으로 사용할 수 있도록하여 두 형식 모두
기존 case … :레이블 (전파 포함)
또는 새 case … ->레이블 (전파 없음)을 사용하고,
switch표현 새 레이블을 사용하여 값을 산출합니다.
이러한 switch 변경 사항은 일상적인 코딩을 단순화하고의 패턴 일치 사용 방법을 준비합니다.
이것은 JDK 12 및 JDK 13 의 미리보기 언어 기능 이었습니다 .

History

스위치 표현은 2017 년 12 월 JEP 325에 의해 제안되었습니다 .
JEP 325는 미리보기 기능으로 2018 년 8 월 JDK 12를 대상 으로했습니다 .
JEP 325의 한 가지 측면은 스위치 표현식에서 결과 값을 리턴하기 위해 명령문을 오버로드하는 것입니다 .
JDK 12에 대한 피드백은 이 사용 이 혼란 스럽다고 제안했습니다 .
피드백에 대한 응답으로, JEP 354 는 JEP 325의 진화로 작성되었습니다.
JEP 354는 새로운 성명을 제안하고 의 원래 의미를 복원했습니다.
JEP 354는 2019 년 6 월 JDK 13을 겨냥했습니다.break break yield break 미리보기 기능으로.
JDK 13에 대한 피드백은 스위치 표현식이 JDK 14에서 더 이상 변경되지 않고 최종적이고 영구적이 될 준비가되었음을 제안했습니다.

Motivation

패턴 일치 (JEP 305) 를 지원하기 위해 Java 프로그래밍 언어를 향상시킬 준비를 할 때 기존 switch문장 의
몇 가지 불규칙성 ( 오랫동안 사용자에게 자극을 주었던)이 장애가됩니다.
여기에는 스위치 레이블 사이의 기본 제어 흐름 동작 (통과),
스위치 블록의 기본 범위 지정 (전체 블록이 하나의 범위로 처리됨) 및 switch 더 자연스럽지만
명령문으로 만 작동 하는 사실이 포함 됩니다.
다 방향 조건을 표현식으로 표현하십시오.

현재 Java switch문장 의 설계는 C 및 C ++와 같은 언어를 밀접하게 따르며 기본적으로 시맨틱를 지원합니다.
이 전통적인 제어 흐름은 switch높은 수준의 컨텍스트에서 사용되는
낮은 수준의 코드 (예 : 이진 인코딩 파서)를 작성하는 데 유용 하지만 오류가 발생하기 쉬운 특성이 유연성을 능가하기 시작합니다.
예를 들어, 다음 코드에서 많은 break 명령문은 불필요하게 장황하게 만들며,이 시각적 노이즈는 종종 오류를 디버그하기 어렵게합니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

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


case L ->레이블이 일치하면 레이블 오른쪽에있는 코드 만 실행됨을 나타 내기 위해 새로운 형식의
스위치 레이블 “case L ->” 을 도입 할 것을 제안합니다 .
또한 사례 당 여러 상수를 쉼표로 구분하여 허용 할 것을 제안합니다.

이전 코드를 작성할 수 있습니다 :

1
2
3
4
5
6
7
8

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

“ case L ->”스위치 레이블 의 오른쪽에있는 코드 는 표현식, 블록 또는 (편의를 위해) throw명령문으로 제한됩니다.
이는 arms 이 로컬 변수를 도입해야하는 즐거운 결과를 가져 오며 블록에 포함되어야하고
따라서 스위치 블록의 다른 arms 에 대한 범위에 속하지 않습니다.
이것은 지역 변수의 범위가 전체 블록 인 전통적인 스위치 블록에 대한 또 다른 성가심을 제거합니다.

1
2
3
4
5
6
7
8
9
10
11
12
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'
}

기존의 많은 switch 명령문은 본질적으로 switch표현식의 시뮬레이션으로, 각 팔은 공통 대상 변수에 지정하거나 값을 반환합니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

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

이것을 진술로 표현하는 것은 우회적이며 반복적이며 오류가 발생하기 쉽습니다.
저자는 numLetters매일 의 가치를 계산해야한다고 표현했습니다 .
보다 명확하고 안전한 switch expression을 사용하여 직접 말할 수 있어야합니다 .

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

결과적으로 switch표현식을 지원하도록 확장 하면 흐름 분석 확장
(표현식이 항상 값을 계산하거나 갑자기 완료해야 함)과 같은 일부 추가 요구 사항이 발생하고
표현식의 일부 사례 switch가 값을 산출하지 않고 예외를 처리 할 수 있습니다.

Description

Arrow labels

case L :스위치 블록의 기존 “ “레이블 외에도 “ “레이블을 사용하여 새로운 단순화 된 양식을 정의합니다
case L ->. 레이블이 일치하면 화살표 오른쪽의 표현식 또는 명령문 만 실행됩니다.
넘어지지 않습니다.
예를 들어, switch새로운 형식의 레이블을 사용하는 다음 명령문이 제공됩니다.

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

다음 코드 :

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

결과는 다음과 같습니다.

1
2
3
one
two
many

Switch expressions

우리는 switch문장을 표현식으로 사용할 수 있도록 확장합니다 .
예를 들어, 표현식 howMany을 사용 하도록 이전 메소드를 다시 작성할 수 있으므로 switch단일 메소드 만 사용합니다 println.

1
2
3
4
5
6
7
8
9
10
11

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

일반적인 경우 switch식은 다음과 같습니다.

1
2
3
4
5
6
7

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

switch식은 폴리 표현이다;
대상 유형이 알려진 경우이 유형은 각 팔로 아래로 밀립니다.
switch식 의 유형은 알려진 경우 대상 유형입니다.
그렇지 않은 경우 독립형 유형은 각 케이스 암의 유형을 결합하여 계산됩니다.

Yielding a value

대부분의 switch표현식에는 “ case L ->”스위치 레이블 오른쪽에 단일 표현식이 있습니다.
전체 블록이 필요한 yield경우 값을 생성하는 새 명령문을 도입 하여 둘러싸는 switch표현식 의 값이됩니다 .

1
2
3
4
5
6
7
8
9
10
11

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

switch표현은 같은 수 switch문, 또한 “전통적인 스위치 블록을 사용하는
case L:”스위치 라벨 (의미를 통해 가을을 의미). 이 경우 새 yield명령문을 사용하여 값이 산출됩니다 .

1
2
3
4
5
6
7
8
9
10
11

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

break(라벨을 포함하거나 포함하지 않은) 두 개의 진술 yield은 switch진술과 switch표현 사이의 명확한 명확성을 용이하게 한다
표현은 switch아니지만 switch표현은 break진술 의 대상이 될 수있다 .
그리고 문장은 switch아니지만 표현은 switch문장의 대상이 될 수 있습니다 yield.

키워드 yield가 아닌 제한된 식별자 (예 var:) yield는 이름 이 지정된 클래스 가 불법 임을 의미합니다 .
yield범위에 단항 메소드가있는 경우 , 표현식 yield(x)은 모호하며
(메소드가 괄호로 묶인 표현식 인 메소드 호출 또는 yield 문일 수 있음),이 모호함은 yield 명령문을 위해 해결됩니다.
메소드 호출이 선호 this되는 경우 인스턴스 메소드 또는 정적 메소드의 클래스 이름으로 메소드를 규정해야합니다 .

Exhaustiveness

switch표현 의 경우 철저 해야합니다 .
가능한 모든 값에 대해 일치하는 스위치 레이블이 있어야합니다. (명쾌하게 switch진술 할 필요는 없습니다.)

실제로 이것은 일반적으로 default조항이 필요함을 의미 합니다.
그러나 enum switch알려진 모든 상수를 포함 하는 표현식 의 경우 default,
enum정의가 컴파일 시간과 런타임 사이에 변경 되었음을 나타내는 절이 컴파일러에 의해 삽입됩니다 .
이 암시 적 default절 삽입 에 의존 하면보다 강력한 코드가 만들어집니다.
이제 코드가 다시 컴파일 될 때 컴파일러는 모든 사례가 명시 적으로 처리되는지 확인합니다.
개발자가 명시 적 default절 (오늘과 마찬가지로)을 삽입 한 경우 가능한 오류가 숨겨져 있습니다.

또한 switch표현식은 값을 사용하여 정상적으로 완료되거나 예외를 발생시켜 갑자기 완료되어야합니다.
이것은 많은 결과를 초래합니다. 먼저, 컴파일러는 모든 스위치 레이블에 대해 일치하는지 확인한 후 값을 얻을 수 있는지 확인합니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

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

또 다른 결과는 제어 문,이다 break, yield, return과 continue, 스루 점프 할 수없는 switch등 다음과 같이 표현 :

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

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

이 JEP는 JEP 325 및 JEP 354 에서 발전했습니다 . 그러나이 JEP는 독립형이며이 두 JEP에 의존하지 않습니다.

JEP 305로 시작하는 패턴 일치에 대한 향후 지원은 이 JEP를 기반으로합니다.

Risks and Assumptions

레이블이 있는 switch진술 의 필요성 case L ->은 불분명합니다. 다음 고려 사항이 포함을 지원했습니다.

  • switch부작용으로 운영하지만, 일반적으로 여전히 “라벨 당 하나 개의 행동”이다 문이 있습니다.
    새로운 스타일의 레이블을 사용하여 접을 수있게하면 문장이보다 간단하고 오류 발생이 줄어 듭니다.

  • switch문장 블록 의 기본 제어 흐름은 깨지기보다는 넘어가는 것이 Java 역사 초기에
    불행한 선택이었으며 개발자에게 계속 큰 문제가되고 있습니다.
    표현식 switch뿐만 아니라 일반적으로 구성에 대해이 문제를 해결하면 switch이 선택의 영향이 줄어 듭니다.

  • 직교 특징으로 원하는 이점 (표현성, 더 나은 제어 흐름, 더 넓은 범위 지정)을
    괴롭 히면 switch표현과 switch진술이 더 공통적 일 수 있습니다.
    switch표현과 표현 의 차이가 클수록 switch언어를 배우는 것이 더 복잡해지고 개발자가 스스로 이해하기가 더 예리합니다.

참조