java-beginner.com ブログ

プログラミングを学習するブログ(Javaをメインに)

switch文のキーにInteger型を使う場合はNullPointerExceptionに注意

投稿日:

最終更新日:2016年06月23日

アイキャッチ

こんにちは。今回はJavaのwhile文についてです。このブログでは以前に、以下の記事でwhile文の基本的な記述方法について書きました。

while文の式(キー)にはプリミティブ型char, byte, short, intを使うことができます。上記の記事ではwhile文のキーにint型を使いました。今回はwhile文のキーにint型のラッパークラスであるInteger型を使ってみました。

この記事の前半ではInteger型について、簡単な説明を書きました。後半ではwhile文の式にInteger型を使った時の動作について書きました。

Integer型の簡単な説明

Javaの変数には大きな分類としてプリミティブ型と参照型があります。

プリミティブ型変数は値を直接保持します。プリミティブ型のひとつとしてint型があります。int型変数は整数値を格納できる型です。(値の範囲については省略します。)

以下はint型変数に値を格納して出力するだけの簡単なサンプルです。

int型の使用例

int my_value = 1;
System.out.println(my_value);

System.out.println("-------------------");

my_value = 2;
System.out.println(my_value);

結果

1
-------------------
2

上記サンプルのように、int型変数は値を代入できます。

Javaにはプリミティブ型に対してラッパークラスが用意されています。ラッパークラスは参照型です。int型のラッパークラスはInteger型です。

以下は簡単な動作確認です。

ソース

Integer my_value = Integer.valueOf(1);   // (1)
System.out.println(my_value.getClass()); // (2)
System.out.println(my_value.intValue()); // (3)
System.out.println(my_value);            // (4)

System.out.println("------------------------------");

my_value = 2;		                     // (5)
System.out.println(my_value);            // (6)

System.out.println("------------------------------");
my_value = null;                         // (7)
System.out.println(my_value);            // (8)

結果

class java.lang.Integer
1
1
------------------------------
2
------------------------------
null

(1)のInteger.valueOf()メソッドは指定されたint値を表すIntegerインスタンスを返します。このメソッドはキャッシュを利用します。そのため、newを使ってコンストラクタを呼び出すより、このメソッドを使った方が良いです。

(2)ではgetClass()メソッドでクラス名を取得しています。実行結果の通り、変数my_valueに格納されているのはInteger型のインスタンスです。このインスタンスが値を保持しています。

(3)のintValue()メソッドはIntegerの値をintとして返します。

(4)ではprintln()メソッドの実引数に直接my_valueを指定しています。この場合、Integer.toString()メソッドが実行されます。このメソッドはIntegerの値を文字列として返します。

(5)のように代入式の右辺に数値を記述できます。コンパイラ側でラッパークラスに自動変換されます。ただし、Java1.4以前ではエラーになるようです。(6)の出力結果を見ると、(5)で値2を持つInteger型のインスタンスが生成されたことがわかります。

(7)では変数my_valueにnullを格納しています。Integer型は参照型なので変数にnullを格納できます。(8)の出力結果を見ると、nullという文字列が出力されています。println()メソッドは引数にnullを指定すると「null」という文字列を出力します。

while文でInteger型を使ったときの動作

while文でInteger型が使えることを以下のサンプルを作って確認しました。

ソース

public class Test001 {

	public static void main(String[] args) {
		Integer integer = Integer.valueOf(1);
		System.out.println(getMsg(integer));

		integer = Integer.valueOf(2);
		System.out.println(getMsg(integer));
	}

	private static String getMsg(Integer integer) {
		String msg = null;

		switch (integer) {
		case 1:
			msg = "CASE 1の処理です。";
			break;

		default:
			msg = "デフォルトの処理です。";
			break;
		}

		return msg;
	}

}

結果

CASE 1の処理です。
デフォルトの処理です。

上記サンプルのgetMsg()メソッドではswitch文のキーに仮引数のInteger型変数integerを指定しています。

main()メソッドではgetMsg()メソッドを呼び出しています。最初はIntegerの値を1にして、引数に指定しています。次にIntegerの値を2にして、引数に指定指定しています。実行結果を見ると、問題なく動作しているのが確認できました。

while文でNullPointerExceptionが発生する場合

Integer型変数にはnullを格納できます。そのため、getMsg()メソッドのswitch文では、キーがnullの可能性があります。その場合の動作を以下のサンプルを作って確認しました。

ソース

package test_20220816_switch_integer.sample;

public class Test002 {

	public static void main(String[] args) {
		Integer integer = Integer.valueOf(1);
		System.out.println(getMsg(integer));

		integer = Integer.valueOf(2);
		System.out.println(getMsg(integer));

		integer = null;                      // (1)
		System.out.println(getMsg(integer)); // (2)
	}

	private static String getMsg(Integer integer) {
		String msg = null;

		switch (integer) {
		case 1:
			msg = "CASE 1の処理です。";
			break;

		default:
			msg = "デフォルトの処理です。";
			break;
		}

		return msg;
	}

}

結果

CASE 1の処理です。
デフォルトの処理です。
Exception in thread "main" java.lang.NullPointerException: Cannot invoke "java.lang.Integer.intValue()" because "integer" is null
	at test_20220816_switch_integer.sample.Test002.getMsg(Test002.java:19)
	at test_20220816_switch_integer.sample.Test002.main(Test002.java:13)

(1)でInteger型変数integerにnullを格納し、その変数を(2)でgetMsg()メソッドの引数に指定しています。

結果からわかるように、switch文のキーがnullの場合、NullPointerExceptionが発生します。

エラーが発生しないようにするためには、例えば、以下のようにswitch文の前にnullチェックをする方法があります。

ソース

package test_20220816_switch_integer.sample;

public class Test003 {

	public static void main(String[] args) {
		Integer integer = Integer.valueOf(1);
		System.out.println(getMsg(integer));

		integer = Integer.valueOf(2);
		System.out.println(getMsg(integer));

		integer = null;                      // (1)
		System.out.println(getMsg(integer)); // (2)
	}

	private static String getMsg(Integer integer) {
		// 引数がnullの場合、nullをリターン
		if (integer == null) {
			return null;
		}

		String msg = null;

		switch (integer) {
		case 1:
			msg = "CASE 1の処理です。";
			break;

		default:
			msg = "デフォルトの処理です。";
			break;
		}

		return msg;
	}

}

結果

CASE 1の処理です。
デフォルトの処理です。
null

上記のgetMsg()メソッドでは最初に引数がnullであるかどうかをif文でチェックしています。引数がnullの場合、nullをリターンしています。return文が実行されるので、メソッドの処理はその行で終了です。

実行結果を見ると、例外が発生していないことがわかります。

上記はメソッドの引数にしている変数にnullが格納されていた場合ですが、そのほかの可能性として、Integer型配列の要素を使っている場合を思いつきました。

以下のサンプルでは、配列をfor文を使って、switch文のキーに配列の要素を指定しています。

ソース

package test_20220816_switch_integer.sample;

public class Test004 {

	public static void main(String[] args) {
		Integer[] integers = {
				Integer.valueOf(1), Integer.valueOf(2),
				Integer.valueOf(3), null };

		for (int i = 0; i < integers.length; i++) {
			System.out.println("i:" + i);
			System.out.println(getMsg(integers[i]));
		}
	}

	private static String getMsg(Integer integer) {
		String msg = null;

		switch (integer) {
		case 1:
			msg = "CASE 1の処理です。";
			break;

		case 2:
			msg = "CASE 2の処理です。";
			break;

		default:
			msg = "デフォルトの処理です。";
			break;
		}

		return msg;
	}

}

結果

i:0
CASE 1の処理です。
i:1
CASE 2の処理です。
i:2
デフォルトの処理です。
i:3
Exception in thread "main" java.lang.NullPointerException: Cannot invoke "java.lang.Integer.intValue()" because "integer" is null
	at test_20220816_switch_integer.sample.Test004.getMsg(Test004.java:19)
	at test_20220816_switch_integer.sample.Test004.main(Test004.java:12)

上記サンプルでは最初にinteger型配列integersを定義しています。想定としては、例えば画面側から年齢の一覧などを受け取ってintegersに配列として受け取っているという状況です。上記では配列の要素のうち、最初の3つはインスタンスです。4つ目がnullです。画面側で未入力のため、nullを受け取ったという想定です。

for文ではgetMsg()メソッドを呼び出しています。引数にintegersのi番目の要素を指定しています。

上記サンプルのgetMsg()メソッドには引数のnullチェックは実装してません。実行結果を見ると、iの値が3のときNullPointerExceptionが発生しました。

このように、integer型配列の要素にはnullが設定できるため、注意が必要です。

以上、参考になれば幸いです。