Code Fix

中級

ClassCastException の原因と直し方

キャストは宣言にすぎず、実体が違えば実行時に落ちます。instanceofによる事前確認を扱います。

エラーメッセージの読み方

Main.java:4: error: incompatible types: String cannot be converted to Integer
Main.java
ファイル名
4
行番号 — javacが異常に気づいた位置。原因の行とは限りません
error
種別 — コンパイルエラー。実行まで到達しません
incompatible types: String cannot be converted to Integer
内容 — javacが期待していたもの、または見つけられなかったもの

このエラーが出る典型パターン

パターン1

 1  public class Main {
 2      public static void main(String[] args) {
 3          Object o = "java";
 4          Integer  s = (String) o;
                ^
 5          System.out.println(s);
 6      }
 7  }
Main.java:4: error: incompatible types: String cannot be converted to Integer

キャストの結果の型と受け取る変数の型は一致させます。ここはコンパイル時に検出できます。

直し方: Integer を String にします。

この問題を解いてみる →

広告
広告スロット(未設定)

パターン2

 1  public class Main {
 2      public static void main(String[] args) {
 3          Object o = "java";
 4          Integer n = (Integer) o;
                            ^
 5          System.out.println(n);
 6      }
 7  }
Exception in thread "main" java.lang.ClassCastException: class java.lang.String cannot be cast to class java.lang.Integerexited with code 1

キャストは「そう扱う」という宣言にすぎません。実体が違えば実行時に落ちます。instanceofで事前に確認します。

直し方: Integer を Integer にします。

この問題を解いてみる →

パターン3

 1  public class Main {
 2      public static void main(String[] args) {
 3          Object o = "java";
 4          if (o is         String) {
                       ^
 5              System.out.println(((String) o).length());
 6          }
 7      }
 8  }
Main.java:4: error: ')' expected

型を確認する演算子はinstanceofです。キャストの前に挟むとClassCastExceptionを防げます。

直し方: is を instanceof にします。

この問題を解いてみる →

パターン4

 1  public class Main {
 2      public static void main(String[] args) {
 3          Object o = Integer.valueOf(5);
 4          Double d = (Double) o                 ;
                                    ^
 5          System.out.println(d);
 6      }
 7  }
Exception in thread "main" java.lang.ClassCastException: class java.lang.Integer cannot be cast to class java.lang.Doubleexited with code 1

IntegerとDoubleはどちらもNumberのサブクラスですが、互いに直接キャストはできません。一度Integerとして受けてからdoubleValue()で変換します。

直し方: (Double) o を ((Integer) o).doubleValue() にします。

この問題を解いてみる →

パターン5

 1  public class Main {
 2      static class Animal {}
 3      static class Dog extends Animal {}
 4      static class Cat extends Animal {}
 5      public static void main(String[] args) {
 6          Animal a = new Dog();
 7          Animal c = (Cat   ) a;
                           ^
 8          System.out.println(c);
 9      }
10  }
Exception in thread "main" java.lang.ClassCastException: class Main$Dog cannot be cast to class Main$Catexited with code 1

実体はDogなのにCatへキャストしようとしています。実際のインスタンスと違う兄弟クラスへはキャストできません。

直し方: Cat を Dog にします。

この問題を解いてみる →

よくある誤解

コンパイルが通ったキャストほど危険です。javacは「あり得るかどうか」しか見ておらず、実際の中身は知りません。

実務での勘所

Java 16以降ではinstanceofにパターンマッチングが導入され、if (obj instanceof String s) { ... }のように書けます。従来のif (obj instanceof String) { String s = (String) obj; ... }と違い、判定と変数への代入が1つの式にまとまるため、判定だけして代入を書き忘れる、あるいは違う変数にキャストするといった手違いが起きなくなります。新しいコードではこちらを使うのが基本になりつつあります。

演習をはじめる

関連するエラー

広告
広告スロット(未設定)