137

Java 7switchステートメントがケースをサポートせず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これにより、すべての使用前に null チェックの条件が回避されswitchます。

4

9 に答える 9

152

damryfbfnetsiがコメントで指摘しているように、 JLS §14.11には次の注記があります。

スイッチ ラベルとしての使用を禁止するnullことで、決して実行できないコードを書くことを防ぎます。式がswitch参照型、つまりStringボックス化されたプリミティブ型または列挙型の場合、実行時に式が評価されるnullと実行時エラーが発生します。Java プログラミング言語の設計者の判断では、これは、ステートメント全体を黙ってスキップしたり、ラベル (存在する場合switch) の後にステートメント (存在する場合) を実行することを選択したりするよりも良い結果です。default

(私のものを強調)

最後の文は を使用する可能性をスキップしてcase null:いますが、これは合理的であり、言語設計者の意図を理解するのに役立ちます。

実装の詳細を見ると、 Christian Hujer によるこのブログ投稿nullには、スイッチで が許可されていない理由について洞察に満ちた推測が含まれています (ただし、enumスイッチではなくスイッチに重点が置かれていますString)。

内部的には、switchステートメントは通常、tableswitch バイト コードにコンパイルされます。そして、「物理的」引数とswitchそのケースはints です。オンにする int 値は、メソッドを呼び出すことによって決定されEnum.ordinal()ます。[...] 序数はゼロから始まります。

つまり、へのマッピングnull0お勧めできません。最初の列挙値の切り替えは、null と区別できません。列挙型の序数を 1 から数え始めるのは良い考えだったかもしれませんが、そのように定義されておらず、この定義は変更できません。

Stringスイッチは異なる方法で実装されていますが、enumスイッチが最初に登場し、参照がnull.

于 2013-08-15T23:30:54.833 に答える
38

一般的nullに扱いにくいです。より良い言語は がなくても生きていけるかもしれませんnull

あなたの問題は次の方法で解決されるかもしれません

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

    }
于 2013-08-16T02:07:17.037 に答える
29

きれいではありませんがString.valueOf()、スイッチでヌル文字列を使用できます。が見つかった場合は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;
于 2015-04-26T02:45:00.117 に答える
16

これは、スローする理由に答える試みですNullPointerException

以下の javap コマンドの出力はcase、引数文字列のハッシュコードに基づいて が選択されていることを示しているため、null 文字列で が呼び出されると switchNPE がスローされます。.hashCode()

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

つまり、 Java の hashCode は異なる文字列に対して同じ値を生成できますか?への回答に基づいています。、まれではありますが、2 つのケースが一致する可能性があります (同じハッシュ コードを持つ 2 つの文字列)。以下の例を参照してください。

    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"andに対して生成されるケースは 1 つだけですが、各ケース文字列との一致をチェックする"FB"2 つの条件があります。ifこの機能を実装する非常に興味深い複雑な方法です。

于 2013-08-16T00:22:56.830 に答える
8

簡単に言えば...(そしてうまくいけば十分に面白い!!!)

Enum はJava1.5で最初に導入され( 2004年 9 月)、文字列の切り替えを許可するよう要求するバグはずっと前 ( 10 月'95 )に提出されました。2004年 6 月にそのバグに投稿されたコメントを見ると、このバグを延期 (無視) し、最終的に Java 1.5 を立ち上げたのと同じ年に、序数が0 から始まる「列挙型」を導入し、決定 (逃した)列挙型の null をサポートしない。Java1.7 ( 2011年 7 月)の後半で、彼らは (強制的にDon't hold your breath. Nothing resembling this is in our plans.) String と同じ哲学 (つまり、バイトコードの生成中に、hashcode() メソッドを呼び出す前に null チェックは実行されませんでした)。

つまり、列挙型が最初に来て、その序数が 0 で始まるように実装されたため、switch ブロックで null 値をサポートできず、後で String で同じ哲学を強制することにしたという事実に要約すると思います。つまり、null 値ではありませんスイッチ ブロックで許可されます。

TL;DR String を使用すると、Java コードからバイトコードへの変換を実装する際に NPE (null のハッシュコードを生成しようとする試みが原因) を処理できたはずですが、最終的には処理しないことにしました。

参照: TheBUGJavaVersionHistoryJavaCodeToByteCodeSO

于 2015-06-30T17:00:35.177 に答える
1

Java ドキュメントによると:

スイッチは、byte、short、char、および int プリミティブ データ型で機能します。また、列挙型 (列挙型で説明)、String クラス、および特定のプリミティブ型をラップするいくつかの特別なクラス (Character、Byte、Short、および Integer (数値と文字列で説明)) でも機能します。

nullタイプがなく、何のインスタンスでもないため、switch ステートメントでは機能しません。

于 2013-08-15T23:35:42.583 に答える
0

答えは簡単です。参照型 (ボックス化されたプリミティブ型など) でスイッチを使用する場合、式が null の場合、ボックス化を解除すると NPE がスローされるため、実行時エラーが発生します。

したがって、null の場合 (これは違法です) はとにかく実行できませんでした;)

于 2013-08-15T23:33:17.533 に答える
0

Apache StringUtils クラスを使用する

String month = null;
switch (StringUtils.trimToEmpty(month)) {
    case "xyz":
        monthNumber=1;  
    break;
    default:
       monthNumber=0;
    break;
}
于 2019-04-04T07:31:28.240 に答える