プログラムdeタマゴ

nodamushiの著作物は、文章、画像、プログラムにかかわらず全てUnlicenseです

Java7のswitch(String)構文のJavaコンパイラーとEclipseコンパイラーの違い

Eclipse3.7.1が今日正式にリリースされました。これでEclipse上でもJava7構文が大手を振って使えます。
さて、Java7で投入されたswitchにStringが使えるという構文。

switch(s){
case "abc":
	System.out.println("ok");break;
case "bbb":
	System.out.println("no");break;
}

これをjavacでコンパイルし、逆コンパイルすると以下の様になります。

byte byte0 = -1;
switch(s1.hashCode()){
case 96354: 
   if(s1.equals("abc"))
       byte0 = 0; break;

case 97314: 
   if(s1.equals("bbb"))
       byte0 = 1; break;
}
switch(byte0){
   case 0: 
       System.out.println("ok");break;

   case 1: 
       System.out.println("no"); break;
}

この様にswitchが2段階で行われます。

Eclipseでコンパイルすると様相が異なります。

switch((s1 = s).hashCode()){
default:
    break;

case 96354: 
    if(s1.equals("abc"))
        System.out.println("ok");
    break;

case 97314: 
    if(s1.equals("bbb"))
        System.out.println("no");
    break;
}

JavaコンパイラーもEclipseみたいに書けばいいじゃんとか思ってたんだけど、よくよく考えてみたら話は単純だった。
以下の様な場合はjavacの様にしてないとうまくいかない(もしくはif文内部に無駄な処理ができる)

switch(s){
case "no"://hashCode=3521
	System.out.println("no");
case "oP"://hashCode=3521
	System.out.println("oP");
	break;
}

sがnoならば、
no
oP
と出力し、oPならばoPだけを出力する。noとoPのハッシュコードは3521で同じである。
この処理、if文で書くとnoの方にbreakがないから、すこし無理をした形にしなければならない。
しかし、javacの様な処理にしておけば、以下の様な素直な形にできる。

switch(s1.hashCode()){
case 3521:
	if(s1.equals("oP"))
		byte0=1;
	else if(s1.equals("no"))
		byte0=0;
	break;
}
switch(byte0){
case 0:
	System.out.println("no");
case 1:
	System.out.println("oP");
default:
	return;
}

ちなみに、Eclipseを逆コンパイルすると、そのいびつな形が出てくる。

switch((s1=s).hashCode()){
default:
	break;

case 3521:
	if(!s1.equals("no")){
		if(!s1.equals("oP"))break;
	}else{
		System.out.println("no");
	}
	System.out.println("oP");
	break;
}

どっちが効率が良いのかまでは分からないけど、単純にSwitch文をSwitch文に直すという意味ではjavacの方が素直な実装になっているように見えるよね。


switch関連で読むと面白い記事
switch文は、case式の書き方によって、lookupswitch命令か、tableswitch命令のいずれかにコンパイルされる模様。