javaでコーディングしていると「変数が初期化されていない可能性があります」というエラーに遭遇する場合がある。今回はどういった場合にこれが発生するのかを深堀りしていく。
発生する様々なパターン
NG
public static void main(String[] args) {
	int n;
	System.out.println(n);
}変数nは宣言だけで初期化されていない。それを出力しようとしているのでエラー
NG
public static void main(String[] args) {
	int n;
	int x=5;
	if(x>0){
		n=10;
	}
	System.out.println(n);
}一見,nに値が入りそうだがコンパイラは条件式に入っている変数内容を調査することはしないのでこれはエラーとなる。
OK
public static void main(String[] args){
    int n;
    final int x = 5;
    if(x > 0){
        n=10;
    }
    System.out.println(n);
}
先ほどとほとんど同じだが、finalをつけて定数とすると条件式に入っていても正常に処理が行われる。
OK
public static void main(String[] args) {
	int n;
	if(5>0){
		n=10;
	}
	System.out.println(n);
}これは条件式部分だけでtrueということがわかる。これは通ると判断されOK
OK
public static void main(String[] args) {
	int n;
	int x=5;
	if(x>0){
		n=10;
	}else{
		n=20;
	}
	System.out.println(n);
}if~else文はどちらかを通ることが保証されている構文なので、この場合は条件式に変数を使っていてもOK
NG
public static void main(String[] args) {
	int n;
	int x=5;
	if(x>0){
		n=10;
	}else if(x<=0){
		n=20;
	}
	System.out.println(n);
}xが0より大きいときとxが0以下の場合にnを初期化している。すべての整数に対してnに値が入るのだが、コンパイラはそれを理解できない(理解しようとしない)よってエラー
OK
public static void main(String[] args) {
	int n;
	int x=5;
	if(x>0){
		n=10;
	}else if(x<0){
		n=20;
	}else{
		n=30;
	}
	System.out.println(n);
}上記のパターンと似ているが最後にelseがあればどれか一つを通ることが保証されるのでOK
NG
public static void main(String[] args) {
	int n;
	int x=5;
	if(x>0){
		n=10;
	}else if(x<0){
		//n=20;コメントアウト
	}else{
		n=30;
	}
	System.out.println(n);
}elseがあっても途中のルートに値が代入されない分岐があるとNG
NG
public static void main(String[] args) {
	int n = new java.util.Random().nextInt(3);//0,1,2の乱数を生成
	String fortune;
	switch(n){
	case 0:
		fortune="大吉";
		break;
	case 1:
		fortune="吉";
		break;
	case 2:
		fortune="凶";
		break;
	}
	System.out.println(fortune);
}上のswitch文もすべての状況が網羅されているが、コンパイラはそれを判断しない、よってNG
OK
public static void main(String[] args) {
	int n = new java.util.Random().nextInt(3);
	String fortune;
	switch(n){
	case 0:
		fortune="大吉";
		break;
	case 1:
		fortune="吉";
		break;
	default:
		fortune="凶";
	}
	System.out.println(fortune);
}if~elseのようにdefaultラベルがあるとすべてのパターンが保証されるのでこれはOK
OK
public static void main(String[] args) {
	int n;
	if(true){
		n=10;
	}
	System.out.println(n);
}条件式にbooleanのリテラルを直接書いた場合は条件式部分のみで判断できるのでOK。
NG
public static void main(String[] args) {
    int n;
    boolean isOk=true;
    if(isOk){
        n=10;
    }
    System.out.println(n);
}
内容的にはほとんど同じだが、変数になるとコンパイラは判断しないのでNG
OK
public static void main(String[] args) {
	int n;
	do{
		n=10;
	}while(false);
	System.out.println(n);
}do~whileは1度は実行されることが保証されている構文なのでOK
OK
public static void main(String[] args) {
	int n;
	for(;;){
		n=3;
		break;
	}
	System.out.println(n);
}for文で無限ループを組んでもコンパイラは通ることを判断できる。これはOK
NG
public static void main(String[] args) {
	int n;
	for(int i=0;i<10;i++){
		n=3;
	}
	System.out.println(n);
}しかし、こんな回ることが自明なループでも継続条件に変数が入ってくるとNG。
まとめ
今回は「初期化されていない可能性があります」というエラーが発生するパターンを実例を中心に解説した。「変数宣言時に必ず初期化すればよい」という考え方もあるが、初期値に何を入れるのかということを考えるのも割とコストの高い作業だ。今回網羅したパターンを理解して、適切に変数の初期化処理を効率化させてもらいたい。
 
  
  
  
  
コメント