JavaのIllegalStateExceptionの原因と対処法を地方フルリモートエンジニアが実務視点で解説

Javaエラー

Javaプログラムを実行していると、IllegalStateExceptionというエラーが発生することがあります。このエラーは、オブジェクトが不正な状態のときに操作を行おうとしたときに発生します。この記事では、IllegalStateExceptionの原因と対処法をわかりやすく解説します。

エラー内容

IllegalStateExceptionは、オブジェクトが不正な状態のときに操作を行おうとしたときに発生する実行時エラー(RuntimeException)です。IllegalArgumentExceptionが引数の問題なのに対し、IllegalStateExceptionはオブジェクトの状態の問題です。

エラーメッセージの例は以下の通りです。

// IllegalStateExceptionが発生する例
import java.util.Scanner;

public class IllegalStateExceptionExample {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        scanner.close();  // Scannerを閉じる
        
        // ここで scanner.nextLine() を呼び出すと
        // IllegalStateException が発生します
        // 閉じた後に読み込もうとするとエラー
        String input = scanner.nextLine();  // IllegalStateException発生
    }
}

実行結果:

Exception in thread "main" java.lang.IllegalStateException: Scanner closed
    at java.util.Scanner.ensureOpen(Scanner.java:1150)
    at java.util.Scanner.nextLine(Scanner.java:1544)
    at IllegalStateExceptionExample.main(IllegalStateExceptionExample.java:8)

Scannerを閉じた後に読み込もうとすると、IllegalStateExceptionが発生します。エラーメッセージには「Scanner closed」と表示されます。このエラーは、オブジェクトが不正な状態のときに操作を行おうとしたときに発生します。

スタックトレースの見方

IllegalStateExceptionが発生したとき、スタックトレース(エラーメッセージ)を読むことで、エラーが発生した箇所を特定できます。スタックトレースの見方を覚えると、原因の判別が早くなります。

スタックトレースの基本構造

スタックトレースは、エラーが発生した場所から、呼び出し元へと順番に表示されます。上から下に向かって、エラーが発生した箇所を追跡できます。

Exception in thread "main" java.lang.IllegalStateException: Scanner closed
    at java.util.Scanner.ensureOpen(Scanner.java:1150)
    at java.util.Scanner.nextLine(Scanner.java:1544)
    at IllegalStateExceptionExample.main(IllegalStateExceptionExample.java:8)

このスタックトレースの見方:

  • 1行目: エラーの種類(IllegalStateException)とスレッド名(main)、エラーの詳細(Scanner closed)
  • 2-3行目: 内部メソッドでのエラー発生箇所(Scanner.nextLine内)
  • 4行目: あなたのコードでエラーが発生した箇所(IllegalStateExceptionExample.main)と行番号(8行目)

重要なポイント

スタックトレースを見るときは、一番下の行(あなたのコード)を確認することが重要です。この例では、`IllegalStateExceptionExample.java:8`が、あなたのコードでエラーが発生した箇所です。

この行番号(8行目)を見ると、`scanner.nextLine()`を呼び出した行でエラーが発生したことがわかります。スタックトレースを読めるようになると、エラーの原因を素早く特定できます。

何が原因か

IllegalStateExceptionが発生する根本的な原因は、オブジェクトが不正な状態のときに操作を行おうとすることです。

IllegalArgumentExceptionが引数の問題なのに対し、IllegalStateExceptionはオブジェクトの状態の問題です。オブジェクトがまだ初期化されていない状態、既に終了した状態、または操作が許可されていない状態で操作を行おうとすると、このエラーが発生します。

よくある原因パターン

IllegalStateExceptionが発生する主な原因パターンを、具体的なコード例で確認しましょう。

パターン1: リソースを閉じた後に操作する

最も多い原因は、リソース(Scanner、Iteratorなど)を閉じた後に操作することです。

// ここで Iterator が終了した後に next() を呼び出すと
// IllegalStateException が発生します
import java.util.Iterator;
import java.util.ArrayList;

public class IteratorExample {
    public static void main(String[] args) {
        ArrayList<String> list = new ArrayList<>();
        list.add("A");
        list.add("B");
        
        Iterator<String> it = list.iterator();
        while (it.hasNext()) {
            System.out.println(it.next());
        }
        // Iteratorは終了している
        
        it.next();  // IllegalStateException発生
    }
}
// Iteratorが終了した後にnextメソッドを呼び出すと、IllegalStateExceptionが発生します

Iteratorが終了した後にnextメソッドを呼び出すと、IllegalStateExceptionが発生します。リソースを閉じた後に操作することは、IllegalStateExceptionの主な原因です。

パターン2: 初期化されていない状態で操作する

オブジェクトが初期化されていない状態で操作しようとすると、エラーが発生します。

// ここで初期化されていない状態で操作すると
// IllegalStateException が発生します
public class StateExample {
    private boolean initialized = false;
    
    public void doSomething() {
        if (!initialized) {
            throw new IllegalStateException("初期化されていません");
        }
        System.out.println("処理を実行");
    }
    
    public static void main(String[] args) {
        StateExample obj = new StateExample();
        obj.doSomething();  // IllegalStateException発生
    }
}
// 初期化されていない状態で操作しようとすると、IllegalStateExceptionが発生します

初期化されていない状態で操作しようとすると、IllegalStateExceptionが発生します。初期化されていない状態で操作することは、IllegalStateExceptionの原因になります。

パターン3: コレクションの変更中に操作する

コレクションを変更中に、別の操作を行おうとするとエラーが発生することがあります。

// ここで Iterator でループ中にコレクションを直接変更すると
// IllegalStateException が発生します
import java.util.ArrayList;
import java.util.Iterator;

public class ConcurrentModification {
    public static void main(String[] args) {
        ArrayList<String> list = new ArrayList<>();
        list.add("A");
        list.add("B");
        
        Iterator<String> it = list.iterator();
        while (it.hasNext()) {
            String item = it.next();
            if (item.equals("A")) {
                list.remove(item);  // コレクションを直接変更
                // IllegalStateExceptionまたはConcurrentModificationExceptionが発生
            }
        }
    }
}
// Iteratorでループ中にコレクションを直接変更すると、IllegalStateExceptionやConcurrentModificationExceptionが発生します

Iteratorでループ中にコレクションを直接変更すると、IllegalStateExceptionやConcurrentModificationExceptionが発生します。コレクションの変更中に操作することは、IllegalStateExceptionの原因になります。

正しい対処法(サンプルコード)

IllegalStateExceptionを回避するための正しい対処法を、具体的なコード例とともに解説します。

対処法1: リソースを適切に管理する

try-with-resources文を使用することで、リソースを自動的に管理できます。

// ここで try-with-resources を使用すると
// IllegalStateException は起きません
import java.util.Scanner;

public class TryWithResources {
    public static void main(String[] args) {
        // try-with-resourcesで自動的に閉じられる
        try (Scanner scanner = new Scanner(System.in)) {
            String input = scanner.nextLine();
            System.out.println("入力: " + input);
        }  // ここで自動的にclose()が呼ばれる
        // 閉じた後は使用できない
    }
}
// try-with-resources文を使用すると、リソースが自動的に閉じられ、閉じた後の操作を防げます

try-with-resources文を使用すると、リソースが自動的に閉じられ、閉じた後の操作を防げます。try-with-resourcesを使用することで、IllegalStateExceptionを安全に回避できます。

対処法2: 状態をチェックする

操作前にオブジェクトの状態をチェックすることで、エラーを防げます。

// ここで操作前に状態をチェックすると
// IllegalStateException は起きません
public class StateCheck {
    private boolean initialized = false;
    
    public void initialize() {
        initialized = true;
    }
    
    public void doSomething() {
        if (!initialized) {
            throw new IllegalStateException("初期化されていません。先にinitialize()を呼び出してください");
        }
        System.out.println("処理を実行");
    }
    
    public static void main(String[] args) {
        StateCheck obj = new StateCheck();
        obj.initialize();  // 先に初期化
        obj.doSomething();  // OK
    }
}
// 操作前に状態をチェックすることで、不正な状態での操作を防げます

操作前に状態をチェックすることで、不正な状態での操作を防げます。状態をチェックすることで、IllegalStateExceptionを安全に回避できます。

対処法3: Iteratorで安全に削除する

コレクションの要素を削除する際は、Iteratorのremoveメソッドを使用します。

// ここで Iterator の remove() メソッドを使用すると
// IllegalStateException は起きません
import java.util.ArrayList;
import java.util.Iterator;

public class SafeRemove {
    public static void main(String[] args) {
        ArrayList<String> list = new ArrayList<>();
        list.add("A");
        list.add("B");
        list.add("C");
        
        Iterator<String> it = list.iterator();
        while (it.hasNext()) {
            String item = it.next();
            if (item.equals("B")) {
                it.remove();  // Iteratorのremoveメソッドを使用
            }
        }
        
        System.out.println("残りの要素: " + list);
    }
}

実行結果:

残りの要素: [A, C]

Iteratorのremoveメソッドを使用することで、安全に要素を削除できます。Iteratorで安全に削除することで、IllegalStateExceptionを安全に回避できます。

初心者がハマりやすい注意点

IllegalStateExceptionを扱う際によくある間違いを紹介します。

注意点1: IllegalArgumentExceptionと混同する

IllegalStateExceptionとIllegalArgumentExceptionは混同しやすいですが、原因と対処法が異なります。

// IllegalStateExceptionとIllegalArgumentExceptionの違いを理解する(推奨)
// IllegalArgumentException:引数の値が不正(範囲外、形式が違うなど)
// IllegalStateException:オブジェクトの状態が不正(初期化されていない、閉じられているなど)
// 原因と対処法が異なる
// 両者を同じものと考える(推奨しない)
// IllegalStateExceptionとIllegalArgumentExceptionは同じ
// どちらも引数の問題だから同じ対処法でいい

IllegalArgumentExceptionは引数の値が不正(範囲外、形式が違うなど)、IllegalStateExceptionはオブジェクトの状態が不正(初期化されていない、閉じられているなど)です。原因と対処法が異なるため、違いを理解することが重要です。

注意点2: リソースを閉じた後に使おうとする

リソースを閉じた後に使おうとするのではなく、try-with-resourcesを使用することが重要です。

// try-with-resourcesを使用する(推奨)
try (Scanner scanner = new Scanner(System.in)) {
    String input = scanner.nextLine();  // OK
}  // 自動的に閉じられる
// 閉じた後に使おうとする(推奨しない)
// Scanner scanner = new Scanner(System.in);
// scanner.close();
// String input = scanner.nextLine();  // エラー!

IllegalStateExceptionが出たら、まずオブジェクトの状態を確認しましょう。リソースを閉じた後に使おうとしていないか、初期化されていない状態で操作しようとしていないかをチェックすることが重要です。try-with-resourcesを使用することで、IllegalStateExceptionを安全に回避できます。

まとめ

この記事では、IllegalStateExceptionの原因と対処法を解説しました。リソースの適切な管理、状態のチェック、Iteratorの安全な使用が重要です。

この記事のポイント

  • IllegalStateExceptionはオブジェクトの状態が不正なときに発生する
  • リソースを閉じた後の操作が主な原因
  • try-with-resourcesでリソースを適切に管理する
  • 操作前に状態をチェックする
  • Iteratorで安全に要素を削除する
  • スタックトレースを読めるようになると、エラーの原因を素早く特定できる