String switch 문이 null case를 지원하지 않는 이유는 무엇입니까?


125

Java 7 switch문이 null케이스를 지원하지 않고 대신 던지는 이유가 궁금합니다 NullPointerException. 아래의 주석 처리 된 줄을 참조하십시오 ( 의 Java 자습서 기사에서switch 가져온 예제 ).

{
    String month = null;
    switch (month) {
        case "january":
            monthNumber = 1;
            break;
        case "february":
            monthNumber = 2;
            break;
        case "march":
            monthNumber = 3;
            break;
        //case null:
        default: 
            monthNumber = 0;
            break;
    }

    return monthNumber;
}

이것은 if매번 switch사용 하기 전에 널 체크에 대한 조건을 피했을 것 입니다.


12
우리가 언어를 만든 사람이 아니기 때문에 이것에 대한 결정적인 대답은 없습니다. 모든 대답은 순수한 추측 일 것입니다.
asteri

2
스위치를 켜려고 null하면 예외가 발생합니다. 에 대한 if확인을 수행 한 null다음 switch문으로 이동 합니다.
gparyani 2013-08-15

28
보내는 사람 JLS : Java 프로그램 언어의 디자이너의 판단에서, [A 던지기 NullPointerException로 표현 평가되면 null자동으로 전체 스위치 문을 건너 뛰는 또는 후 (있는 경우) 문을 실행하기 위해 선택하는 것보다 더 나은 결과입니다 런타임]가 기본 레이블 (있는 경우).
gparyani 2013-08-15

3
@gparyani : 답을 만드세요. 그것은 매우 공식적이고 확실하게 들립니다.
Thilo

7
@JeffGohlke : "결정을 내린 사람이 아니라면 이유 질문에 답할 방법이 없습니다." ... 음, gparyani의 코멘트는 달리 증명
user541686

답변:


145

damryfbfnetsi으로 종료 지점 코멘트에, JLS §14.11는 다음과 같은 메모를 가지고 :

null스위치 레이블로 사용하는 것을 금지하는 것은 실행할 수없는 코드를 작성하는 것을 방지합니다. 상기 중간 switch표현은 참조 형식,이다 String또는 박스형 원시 형 또는 열거 형 다음 런타임 에러 발현 평가되는 경우에 발생한다 null런타임. Java 프로그래밍 언어 설계자의 판단에 따르면 전체 switch명령문을 조용히 건너 뛰 거나 default레이블 (있는 경우 ) 뒤에 명령문 (있는 경우)을 실행하도록 선택 하는 것보다 더 나은 결과 입니다.

(강조 광산)

마지막 문장은를 사용할 가능성을 건너 뛰지 만 case null:합리적으로 보이며 언어 디자이너의 의도에 대한 견해를 제공합니다.

구현 세부 사항을 살펴보면 Christian Hujer 의이 블로그 게시물null 에 스위치에서 허용되지 않는 이유 에 대한 통찰력있는 추측 이 있습니다 ( enum스위치가 아닌 스위치에 중점을두고 있음 String).

내부적 switch으로이 문은 일반적으로 tablesswitch 바이트 코드로 컴파일됩니다. 그리고 switch그 사건뿐만 아니라 "물리적"주장 은 ints입니다. 스위치를 켤 int 값은 메서드를 호출하여 결정됩니다 Enum.ordinal(). [...] 서수는 0에서 시작합니다.

즉, 매핑 null하는 0것은 좋은 생각이 아닙니다. 첫 번째 열거 형 값의 스위치는 null과 구별 할 수 없습니다. 1에서 열거 형의 서수를 세는 것이 좋은 생각이었을 것입니다. 그러나 그렇게 정의되지 않았고이 정의는 변경할 수 없습니다.

반면 String스위치 다르게 구현되어 상기 enum스위치는 첫 번째 들어오는 기준 인 경우에 동작한다 참조 형식에 스위칭 방법에 대한 선례 null.


1
.NET case null:전용으로 구현 되었으면 a의 일부로 null 처리를 허용하는 것이 큰 발전 이었을 것입니다 String. 현재 모든 String검사에는에서와 같이 문자열 상수를 먼저 넣음으로써 암시 적으로 대부분 묵시적으로 수행하려는 경우 어쨌든 null 검사가 필요합니다 "123test".equals(value). 이제 우리는 다음과 같이 switch 문을 작성해야합니다if (value != null) switch (value) {...
YoYo

1
다시 "null을 0으로 매핑하는 것은 좋은 생각이 아닙니다.": "".hashcode ()의 값이 0이기 때문에 과소 표현입니다! 이는 null 문자열과 길이가 0 인 문자열이 switch 문에서 동일하게 처리되어야 함을 의미하며, 이는 분명히 실행 가능하지 않습니다.
skomisa

enum의 경우 null을 -1로 매핑하는 것을 막는 것은 무엇입니까?
krispy

31

일반적으로 null다루기 힘들다. 더 나은 언어는 null.

귀하의 문제는

    switch(month==null?"":month)
    {
        ...
        //case "":
        default: 
            monthNumber = 0;

    }

month이 빈 문자열 이면 좋은 생각이 아닙니다 . 이것은 null 문자열과 같은 방식으로 처리됩니다.
gparyani 2013-08-16

13
많은 경우 null을 빈 문자열로 처리하는 것은 완벽하게 합리적 일 수 있습니다.
Eric Woodruff

23

예쁘지는 않지만 String.valueOf()스위치에서 null String을 사용할 수 있습니다. 을 찾으면 null로 변환하고 "null", 그렇지 않으면 전달한 것과 동일한 문자열을 반환합니다. "null"명시 적으로 처리하지 않으면 default. 유일한주의 사항은 문자열 "null"과 실제 null변수 를 구분할 방법이 없다는 것입니다 .

    String month = null;
    switch (String.valueOf(month)) {
        case "january":
            monthNumber = 1;
            break;
        case "february":
            monthNumber = 2;
            break;
        case "march":
            monthNumber = 3;
            break;
        case "null":
            monthNumber = -1;
            break;
        default: 
            monthNumber = 0;
            break;
    }
    return monthNumber;

2
나는 자바에서 이와 같은 일을하는 것이 반 패턴이라고 생각합니다.
Łukasz Rzeszotarski

2
@ ŁukaszRzeszotarski 내가 무슨 뜻인지 꽤 많은 사용자들은 "꽤하지 않습니다"
크리

15

이것은 왜 던지는 지 대답하려는 시도입니다. NullPointerException

아래의 javap 명령의 출력은 인수 문자열 case의 해시 코드를 기반으로 선택한 것을 나타내 switch므로 .hashCode()null 문자열 에서이 호출 될 때 NPE를 발생 시킵니다.

6: invokevirtual #18                 // Method java/lang/String.hashCode:()I
9: lookupswitch  { // 3
    -1826660246: 44
     -263893086: 56
      103666243: 68
        default: 95
   }

이것은 Java의 hashCode가 다른 문자열에 대해 동일한 값을 생성 할 수 있습니까? 에 대한 답변을 기반으로 함을 의미합니다 . , 드물기는하지만 여전히 두 개의 케이스가 일치 할 가능성이 있습니다 (동일한 해시 코드를 가진 두 개의 문자열) 아래이 예를 참조하십시오.

    int monthNumber;
    String month = args[0];

    switch (month) {
    case "Ea":
        monthNumber = 1;
        break;
    case "FB":
        monthNumber = 2;
        break;
    // case null:
    default:
        monthNumber = 0;
        break;
    }
    System.out.println(monthNumber);

어느 Javap

  10: lookupswitch  { // 1
              2236: 28
           default: 59
      }
  28: aload_3       
  29: ldc           #22                 // String Ea
  31: invokevirtual #24                 // Method java/lang/String.equals:(Ljava/lang/Object;)Z
  34: ifne          49
  37: aload_3       
  38: ldc           #28                 // String FB
  40: invokevirtual #24                 // Method java/lang/String.equals:(Ljava/lang/Object;)Z
  43: ifne          54
  46: goto          59 //Default

당신은 단지 하나의 사건이 생성됩니다 볼 수 있듯이을 위해 "Ea"하고 "FB"있지만, 두 가지로 if조건이 각각의 경우 문자열 일치를 확인합니다. 이 기능을 구현하는 매우 흥미롭고 복잡한 방법!


5
하지만 다른 방식으로 구현되었을 수도 있습니다.
Thilo

2
이것이 디자인 버그라고 말하고 싶습니다.
Deer Hunter

6
이것이 문자열 해시 함수의 일부 모호한 측면이 변경되지 않은 이유와 관련이 있는지 궁금합니다. 일반적으로 코드는 hashCode프로그램의 다른 실행에서 동일한 값을 반환하는 데 의존해서는 안되지만 문자열 해시가 실행 파일에 구워지기 때문에 컴파일러에서 문자열 해시 메서드는 언어 사양의 일부가됩니다.
supercat

5

긴 이야기 짧게 ... (그리고 충분히 흥미 로웠기를 바랍니다 !!!)

Enum은 Java1.5 ( 20049 ) 에서 처음 도입되었으며 String 켜기허용 하라는 버그 가 오랫동안 제기되었습니다 ( 95 년 10 월 ). 2004 년 6 월에 해당 버그에 게시 된 댓글을 보면이 버그를 연기 ( 무시 )하고 결국 0부터 시작하는 서수로 '열거 형'을 도입하고 결정한 같은 해에 Java 1.5를 출시 한 것 같습니다 ( missed ) 열거 형에 대해 null을 지원하지 않습니다. 나중에 Java1.7 ( 20117 )에서 그들은 따랐습니다 ( 강제Don't hold your breath. Nothing resembling this is in our plans.) String과 동일한 철학 (즉, 바이트 코드를 생성하는 동안 hashcode () 메서드를 호출하기 전에 null 검사가 수행되지 않음).

따라서 enum이 처음에 들어오고 서수 시작이 0으로 구현되어 스위치 블록에서 null 값을 지원할 수 없었고 나중에 String으로 동일한 철학을 강요하기로 결정했습니다. 스위치 블록에서 허용됩니다.

TL; DR String을 사용하면 Java 코드를 바이트 코드로 변환하는 동안 NPE (null에 대한 해시 코드를 생성하려는 시도로 인해 발생)를 처리 할 수 ​​있었지만 결국 그렇게하지 않기로 결정했습니다.

참조 : TheBUG , JavaVersionHistory , JavaCodeToByteCode , SO


1

Java Docs에 따르면 :

스위치는 byte, short, char 및 int 기본 데이터 유형과 함께 작동합니다. 또한 열거 형 (Enum Types에서 설명), String 클래스, 그리고 Character, Byte, Short 및 Integer (Numbers 및 Strings에서 설명)와 같은 특정 기본 유형을 래핑하는 몇 가지 특수 클래스와 함께 작동합니다.

때문에 null어떤 유형이없고, 아무것도의 인스턴스가 아닌, 그것을 switch 문 작동하지 않습니다.


4
그럼에도 불구하고 nullA에 대한 올바른 값 String, Character, Byte, Short, 또는 Integer참조.
asteri

0

대답은 단순히 참조 유형 (예 : boxed primitive 유형)과 함께 스위치를 사용하는 경우 unboxing이 NPE를 던지기 때문에 표현식이 null이면 런타임 오류가 발생한다는 것입니다.

그래서 케이스 null (불법)은 어쨌든 실행될 수 없습니다.)


1
하지만 다른 방식으로 구현되었을 수도 있습니다.
Thilo

좋아요 @Thilo, 저보다 똑똑한 사람들이이 구현에 참여했습니다. 이 구현되었을 수있는 다른 방법을 알고 있다면, 나는 ... 사람들이 [나는 확실히 다른 사람이있는거야]를 공유 할 알고 싶어요
amrith

3
문자열은 boxed primitive 유형이 아니며 누군가 "unbox"를 시도하기 때문에 NPE가 발생하지 않습니다.
Thilo 2013-08-16

@thilo, 이것을 구현하는 다른 방법은 무엇입니까?
amrith

3
if (x == null) { // the case: null part }
Thilo

0

@Paul Bellora의 답변에 있는 https://stackoverflow.com/a/18263594/1053496 의 통찰력있는 댓글 (Under the hood ....)에 동의합니다 .

내 경험에서 또 하나의 이유를 찾았습니다.

'case'가 null 일 수 있다는 것은 switch (variable)가 null임을 의미하며 개발자가 일치하는 'null'대소 문자를 제공하는 한 괜찮다고 주장 할 수 있습니다. 그러나 개발자가 일치하는 'null'대소 문자를 제공하지 않으면 어떻게 될까요? 그런 다음 개발자가 기본 사례에서 처리하려는 것이 아닐 수도있는 '기본'사례와 일치시켜야합니다. 따라서 'null'을 기본값과 일치 시키면 '놀라운 동작'이 발생할 수 있습니다. 따라서 'NPE'를 던지면 개발자가 모든 경우를 명시 적으로 처리하게됩니다. 이 경우 NPE를 던지는 것은 매우 사려 깊습니다.


0

Apache StringUtils 클래스 사용

String month = null;
switch (StringUtils.trimToEmpty(month)) {
    case "xyz":
        monthNumber=1;  
    break;
    default:
       monthNumber=0;
    break;
}
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.