きどたかのブログ

いつか誰かがこのブログからトラブルを解決しますように。

Java7のString Switch

家で試してみました。

サンプルで書いたコード。
"1a"と"2B"は同一ハッシュコード1616です。

	private String test1(String val) {
		String ret = null;
		switch (val) {
		case "1a":
			ret = "match 1a";
			break;
		case "2B":
			ret = "match 2B";
			break;
		default:
			ret = "match default";
		}
		return ret;
	}


indigoのclass file editorで見たものに、補足を書き足しました。

  private java.lang.String test1(java.lang.String val);
     0  aconst_null     // nullをオペランドスタックに積む
     1  astore_2 [ret]  // オペランドスタックから取り出し、2番目のローカル変数に入れる
     2  aload_1 [val]   // 1番目のローカル変数を、オペランドスタックに積む
     3  dup             // オペランドスタックの一番上に積まれてる参照をもう一個作り、積む。
     4  astore_3        // オペランドスタックから取出し、3番目のローカル変数に入れる。
     5  invokevirtual java.lang.String.hashCode() : int [29]  // オペランドスタックから取り出し、valのハッシュコード取得
     8  lookupswitch default: 61 // ハッシュコードの評価。1616なら28へ、それ以外は61へ飛ぶ。
          case 1616: 28
    28  aload_3           // 3番目のローカル変数を、オペランドスタックに積む。
    29  ldc  [35]   // ベタ書きの"1a"をオペランドスタックに積む。
    31  invokevirtual java.lang.String.equals(java.lang.Object) : boolean [37] // "1a".equals(val)
    34  ifne 49 // trueなら49へ。falseなら37に続く。
    37  aload_3 // 3ばんめのローカル変数を、オペランドスタックに積む。
    38  ldc  [48] // ベタ書きの"2B"をオペランドスタックに積む
    40  invokevirtual java.lang.String.equals(java.lang.Object) : boolean [37] "2B".equals(val)
    43  ifne 55 // trueなら55へ。falseなら46に続く
    46  goto 61 // ハッシュコードがマッチしたが、どの定数にも合致せず、defaultの飛び先である61へ
    49  ldc  [50] // "1a"に合致した際の処理。文字列をオペランドスタックに積む
    51  astore_2 [ret]               // オペランドスタックから取り出し、2番目のローカル変数に代入
    52  goto 64                      // 64へ
    55  ldc  [52] // "2B"に合致した際の処理。文字列をオペランドスタックに積む
    57  astore_2 [ret]               // オペランドスタックから取り出し、2番目のローカル変数に代入
    58  goto 64                      // 64へ
    61  ldc  [54] // defaultになった際の処理。文字列をオペランドスタックに積む
    63  astore_2 [ret]                    // オペランドスタックから取り出し、2番目のローカル変数に代入
    64  aload_2 [ret]                     // returnの処理。2番目のローカル変数をオペランドスタックに積む
    65  areturn                           // オペランドスタックから取り出してreturn。
      Line numbers:
        [pc: 0, line: 27]
        [pc: 2, line: 28]
        [pc: 49, line: 30]
        [pc: 52, line: 31]
        [pc: 55, line: 33]
        [pc: 58, line: 34]
        [pc: 61, line: 36]
        [pc: 64, line: 38]
      Local variable table:
        [pc: 0, pc: 66] local: this index: 0 type: StringSwitch
        [pc: 0, pc: 66] local: val index: 1 type: java.lang.String
        [pc: 2, pc: 66] local: ret index: 2 type: java.lang.String
      Stack map table: number of frames 5
        [pc: 28, append: {java.lang.String, java.lang.String}]
        [pc: 49, same]
        [pc: 55, same]
        [pc: 61, same]
        [pc: 64, chop 1 local(s)]


基本的には言語使用が変更され、コンパイラが賢くなったということですね。


コンパイラが勝手にローカル変数(index 3)を作ることがあるのだと知りました。
そしてLocal variable tableには入ってない。


case文に使用出来るのは定数表現でなければならないとコンパイラーがエラーを出力するのだが、
どのへんまで許容されるか探りを入れてみた。
代入を遅らせて宣言するfinal String var;はどうやらNGのようだ。
代入がメソッドの戻り値でもダメ(finalだけでは判断されない)。final String var = getVar();
クラス変数、インスタンス変数、ローカル変数のどれでも構わない。