kazasiki's blog

プログラミングとかVRゲームとか

Javaの配列に関するちょっとややこしい話

昨日、現場の人のソースを読んでて気になったので指摘したのがあって、それがどんなソースだったのかご紹介。
実際に修正してもらおうとしたところ、その人がしどろもどろになってたのが面白かったので。


私が見たソースはこんなかんじでした。もちろん色々端折ってます。
要するに、「分岐させた先で配列変数に値を設定して、その後に配列変数の値を参照してなんらかの処理をしたい」のです。

private void myMethod(boolean foo){

  // 分岐
  if(foo){
    // 配列変数に値を代入する
    String bar[] = {"月", "Mon" };
		
    // 変数の値を使って何かする。
    System.out.println(bar[0] + "曜日は英語で" +  bar[1] +"です");
  } else {	
    // 配列変数に値を代入する
    String bar[] = {"火", "Tue" };
		
    // 変数の値を使って何かする。 
    System.out.println(bar[0] + "曜日は英語で" +  bar[1] +"です");
  }
}


ぱっと見でわかりますが、上に書いた処理の『// 変数の値を使って何かする。』の部分は同じ処理が分岐の両方に書いてあり無駄があります。
そこで私は実装者にこのような指摘を行いました。


『// 変数の値を使って何かする。 』の部分をif文の下に出してください。


ぱぱっと実装者の人が書いたのはこちら。
分かる人はわかると思いますが、コンパイルエラーがでます。
配列変数barのスコープはif文の各分岐内で切れてしまっているため、分岐の外では使えません。

private void myMethod(boolean foo){

  // 分岐
  if(foo){
    // 配列変数に値を代入する
    String bar[] = {"月", "Mon" };
  } else {	
    // 配列変数に値を代入する
    String bar[] = {"火", "Tue" };		
  }

  // 変数の値を使って何かする。 
  System.out.println(bar[0] + "曜日は英語で" +  bar[1] +"です");
}


ここからは私が順を追って説明しました。
まず、配列変数barのスコープを変えるため、宣言をif文の前に出します。

private void myMethod(boolean foo){

  // 分岐の前に変数を宣言する
  String bar[];

  // 分岐
  if(foo){
    // 配列変数に値を代入する
    bar = {"月", "Mon" };
  } else {	
    // 配列変数に値を代入する
    bar = {"火", "Tue" };		
  }
  // 変数の値を使って何かする。 
  System.out.println(bar[0] + "曜日は英語で" +  bar[1] +"です");
}


一見妥当なように見えますが、まだコンパイルエラーがでます。
分岐処理内で行なっている配列変数への代入は、正常にコンパイルできません。


理由を説明すると、Javaでは配列を生成できるタイミングが限られています。

  1. 配列変数を宣言する時
  2. 配列をnewする時

この2つのタイミングでしか配列を生成することが出来ません。

従って、処理の途中に下のような処理を書き込んでもコンパイルエラーになります。

bar = {"月", "Mon" };


Javaでの配列は int などのプリティブ型と違いオブジェクトとして扱われます。
なので、本来newを使わずにオブジェクトを生成することができません。


「じゃあ配列変数を宣言する時に初期化する場合はなんでnewいらないの?」と言われると、Javaが特別に気を効かせてくれているとしか言えません(笑)
(Stringなんかも特別扱いされてますね)


ということで、Javaで配列を生成したい場合はこうなります。

bar[] = new String[] {"月", "Mon" };

先ほどのプログラムは正しくはこうなります。

private void myMethod(boolean foo){

  // 分岐の前に変数を宣言する
  String bar[];

  // 分岐
  if(foo){
    // 配列変数に値を代入する
    bar = new String[] {"月", "Mon" }; // newを追加
  } else {
    // 配列変数に値を代入する
    bar = new String[] {"火", "Tue" }; // newを追加
  }
  // 変数の値を使って何かする。 
  System.out.println(bar[0] + "曜日は英語で" +  bar[1] +"です");
}


Javaの配列の扱いは特殊なので、詳しい仕様を押さえてない人は
プログラミングをそれなりにやっててもちょっとつまずくのかなと思います。


以上、Javaの配列に関するちょっとややこしい話でした。