raw type とワイルドカードの使い分け
型引数を省くと型安全が失われます。List<Object>とList<?>の違いも整理します。
エラーメッセージの読み方
Main.java:8: error: incompatible types: Object cannot be converted to String
Main.java- ファイル名
8- 行番号 — javacが異常に気づいた位置。原因の行とは限りません
error- 種別 — コンパイルエラー。実行まで到達しません
incompatible types: Object cannot be converted to String- 内容 — javacが期待していたもの、または見つけられなかったもの
このエラーが出る典型パターン
パターン1
1 import java.util.ArrayList; 2 import java.util.List; 3 4 public class Main { 5 public static void main(String[] args) { 6 List list = new ArrayList<>(); ^ 7 list.add("a"); 8 String s = list.get(0); 9 System.out.println(s); 10 } 11 }
Main.java:8: error: incompatible types: Object cannot be converted to String
型引数を省くと生の型になり、要素はObjectとして扱われます。取り出すたびにキャストが必要になります。
直し方: (空) を <String> にします。
広告
広告スロット(未設定)
パターン2
1 import java.util.List; 2 3 public class Main { 4 static void show(List<Object> list) { ^ 5 for (Object o : list) System.out.println(o); 6 } 7 public static void main(String[] args) { 8 List<Integer> nums = List.of(1, 2, 3); 9 show(nums); 10 } 11 }
Main.java:9: error: incompatible types: List<Integer> cannot be converted to List<Object>
List<Integer>はList<Object>の部分型ではありません。何でも受けるにはワイルドカード ? を使います。
直し方: Object を ? にします。
パターン3
1 public class Main { 2 static <T> T first(java.util.ArrayList <T> list) { ^ 3 return list.get(0); 4 } 5 public static void main(String[] args) { 6 System.out.println(first(java.util.List.of("a"))); 7 } 8 }
Main.java:6: error: method first in class Main cannot be applied to given types;
required: ArrayList<T>
found: List<String>
reason: cannot infer type-variable(s) T
引数は最も広い型で受けるのが原則です。ArrayListで受けるとListしか持たない呼び出し側が渡せません。
直し方: java.util.ArrayList を java.util.List にします。
よくある誤解
警告だけでコンパイルは通ります。通ってしまうからこそ、本番で ClassCastException として現れます。
まとめ
raw type とワイルドカードの使い分けは上級でつまずきやすい項目です。上の3パターンを実際に手で直すと、エラーメッセージのどこを読めばよいかが掴めます。