web-dev-qa-db-ja.com

String switchステートメントがnullケースをサポートしないのはなぜですか?

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条件を回避できます。

114
Prashant Bhate

Damryfbfnetsi points out のコメントのように、 JLS§14.11 には次の注記があります。

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

(エンファシス鉱山)

最後の文はcase null:を使用する可能性をスキップしますが、それは合理的であり、言語設計者の意図に対する見解を提供します。

実装の詳細を見ると、 このブログ投稿 Christian Hujerが、スイッチでnullが許可されない理由について洞察力のある推測をしています(ただし、enumスイッチを中心にしています) Stringスイッチではなく):

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

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

Stringスイッチ 異なる実装 の場合、enumスイッチが最初に来て、参照がnullである場合に参照型の切り替えがどのように動作するかについての先例を設定しました。

137
Paul Bellora

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

あなたの問題は

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

    }
27
ZhongYu

きれいではありませんが、String.valueOf()を使用すると、スイッチでNULL文字列を使用できます。 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;
23
krispy

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

以下のjavapコマンドの出力は、caseswitch引数文字列のハッシュコードに基づいて選択されているため、.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は異なる文字列に対して同じ値を生成できますか? への回答に基づくことを意味しますが、まれに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

また、生成されるケースは1つだけですが、各ケース文字列でマッハをチェックするための2つのif条件があります。この機能を実装する非常に興味深い複雑な方法!

14
Prashant Bhate

長い話...(そして、うまくいけば十分に面白い!!!)

Enumは、最初に Java1.5Sep'2004)および bugStringのスイッチを許可することは、長い間ファイルされていました(Oct'95)。そのバグに関するJun'2004に投稿されたコメントを見ると、Don't hold your breath. Nothing resembling this is in our plans.が遅延しているように見える(ignored)このバグは最終的にJava 1.5を開始し、同じ年に0から始まる順序で 'enum'を導入し、(missed)列挙型のnullをサポートしません。後で Java1.7Jul'2011)彼らは(forced)Stringと同じ哲学(つまり、バイトコードの生成中、hashcode()メソッドを呼び出す前にnullチェックは実行されませんでした)。

だから、列挙型が最初に来て、スイッチブロックでnull値をサポートできなかったため、0から始まる順序で実装され、後にStringで同じ哲学、つまりnull値ではないスイッチブロックで許可されます。

TL; DRJavaコードの実装中に、Stringを使用すると、NPE(nullのハッシュコードを生成しようとするため)を処理できます。バイトコード変換に、しかし最終的にしないことにしました。

参照: TheBUGJavaVersionHistoryJavaCodeToByteCodeSO

4
sactiw

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

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

nullは型を持たず、何のインスタンスでもないため、switchステートメントでは機能しません。

1
BlackHatSamurai

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

したがって、ケースnull(これは違法です)を実行することはできません;)

0
amrith

私は洞察に富んだコメント(フードの下で...)に同意します https://stackoverflow.com/a/18263594/1053496 @Paul Belloraの答え。

私の経験からもう一つの理由を見つけました。

'case'がnullになる可能性がある場合、switch(variable)がnullであることを意味し、開発者が一致する 'null'ケースを提供する限り、それは問題ないと主張できます。しかし、開発者が一致する「null」ケースを提供しない場合はどうなりますか。次に、それを「デフォルト」のケースに一致させる必要がありますが、これはデベロッパーがデフォルトのケースで処理しようとしたものではない場合があります。したがって、「null」をデフォルトに一致させると、「驚くべき動作」が発生する可能性があります。したがって、「NPE」をスローすると、開発者はすべてのケースを明示的に処理できます。この場合、NPEを投げることは非常に思慮深いことでした。

0
nantitv

Apache StringUtilsクラスを使用する

String month = null;
switch (StringUtils.trimToEmpty(month)) {
    case "xyz":
        monthNumber=1;  
    break;
    default:
       monthNumber=0;
    break;
}
0
bhagat