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
条件を回避できます。
Damryfbfnetsi points out のコメントのように、 JLS§14.11 には次の注記があります。
null
をスイッチラベルとして使用することを禁止すると、実行できないコードを記述できなくなります。switch
式が参照型、つまりString
またはボックス化されたプリミティブ型または列挙型である場合、式がnull
と評価されると実行時エラーが発生します。実行時に。 Javaプログラミング言語の設計者の判断では、これはswitch
ステートメント全体を静かにスキップするか、ステートメントの実行を選択するよりも良い結果です(ある場合)default
ラベルの後(ある場合)。
(エンファシス鉱山)
最後の文はcase null:
を使用する可能性をスキップしますが、それは合理的であり、言語設計者の意図に対する見解を提供します。
実装の詳細を見ると、 このブログ投稿 Christian Hujerが、スイッチでnull
が許可されない理由について洞察力のある推測をしています(ただし、enum
スイッチを中心にしています) String
スイッチではなく):
内部では、
switch
ステートメントは通常、tablesswitchバイトコードにコンパイルされます。そして、switch
の「物理的」引数とそのケースはint
sです。有効にするint値は、メソッドEnum.ordinal()
を呼び出すことで決定されます。 [...]序数はゼロから始まります。つまり、
null
を0
にマッピングすることはお勧めできません。最初の列挙値の切り替えは、nullと区別できません。たぶん、1で列挙型の序数のカウントを開始することをお勧めします。ただし、そのように定義されておらず、この定義を変更することはできません。
String
スイッチ 異なる実装 の場合、enum
スイッチが最初に来て、参照がnull
である場合に参照型の切り替えがどのように動作するかについての先例を設定しました。
一般に、null
は扱いにくいです。より良い言語はnull
なしで生きることができるかもしれません。
あなたの問題は
switch(month==null?"":month)
{
...
//case "":
default:
monthNumber = 0;
}
きれいではありませんが、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;
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は異なる文字列に対して同じ値を生成できますか? への回答に基づくことを意味しますが、まれに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条件があります。この機能を実装する非常に興味深い複雑な方法!
長い話...(そして、うまくいけば十分に面白い!!!)
Enumは、最初に Java1.5 (Sep'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.7 (Jul'2011)彼らは(forced)Stringと同じ哲学(つまり、バイトコードの生成中、hashcode()メソッドを呼び出す前にnullチェックは実行されませんでした)。
だから、列挙型が最初に来て、スイッチブロックでnull値をサポートできなかったため、0から始まる順序で実装され、後にStringで同じ哲学、つまりnull値ではないスイッチブロックで許可されます。
TL; DRJavaコードの実装中に、Stringを使用すると、NPE(nullのハッシュコードを生成しようとするため)を処理できます。バイトコード変換に、しかし最終的にしないことにしました。
参照: TheBUG 、 JavaVersionHistory 、 JavaCodeToByteCode 、 SO
Javaドキュメントによると:
スイッチは、byte、short、char、およびintのプリミティブデータ型で機能します。また、列挙型(Enum Typesで説明)、Stringクラス、および特定のプリミティブ型をラップするいくつかの特別なクラス(Character、Byte、Short、Integer(NumbersおよびStringsで説明))でも機能します。
null
は型を持たず、何のインスタンスでもないため、switchステートメントでは機能しません。
答えは、参照型(ボックス化されたプリミティブ型など)でスイッチを使用する場合、式がnullであると実行時エラーが発生するためです。
したがって、ケースnull(これは違法です)を実行することはできません;)
私は洞察に富んだコメント(フードの下で...)に同意します https://stackoverflow.com/a/18263594/1053496 @Paul Belloraの答え。
私の経験からもう一つの理由を見つけました。
'case'がnullになる可能性がある場合、switch(variable)がnullであることを意味し、開発者が一致する 'null'ケースを提供する限り、それは問題ないと主張できます。しかし、開発者が一致する「null」ケースを提供しない場合はどうなりますか。次に、それを「デフォルト」のケースに一致させる必要がありますが、これはデベロッパーがデフォルトのケースで処理しようとしたものではない場合があります。したがって、「null」をデフォルトに一致させると、「驚くべき動作」が発生する可能性があります。したがって、「NPE」をスローすると、開発者はすべてのケースを明示的に処理できます。この場合、NPEを投げることは非常に思慮深いことでした。
Apache StringUtilsクラスを使用する
String month = null;
switch (StringUtils.trimToEmpty(month)) {
case "xyz":
monthNumber=1;
break;
default:
monthNumber=0;
break;
}