Map と HashMap を両方importする理由
インターフェースと実装クラスは別物です。受け口を広く、実体を具体的にという原則を扱います。
エラーメッセージの読み方
Main.java:5: error: cannot find symbol
Main.java- ファイル名
5- 行番号 — javacが異常に気づいた位置。原因の行とは限りません
error- 種別 — コンパイルエラー。実行まで到達しません
cannot find symbol- 内容 — javacが期待していたもの、または見つけられなかったもの
このエラーが出る典型パターン
パターン1
1 import java.util.List ; ^ 2 3 public class Main { 4 public static void main(String[] args) { 5 Map<String, Integer> m = new HashMap<>(); 6 m.put("a", 1); 7 System.out.println(m.get("a")); 8 } 9 }
MapとHashMapは別クラスなので両方importが要ります。java.util.* でまとめて取り込めます。
直し方: List を * にします。
パターン2
1 import java.util.ArrayList; 2 import java.util.List; 3 4 public class Main { 5 public static void main(String[] args) { 6 List<String> list = new List <>(); ^ 7 list.add("a"); 8 System.out.println(list); 9 } 10 }
Listはインターフェースなのでnewできません。受け口をインターフェース、実体を具象クラスにするのが定石です。
直し方: List を ArrayList にします。
パターン3
1 import java.util.*; 2 3 public class Main { 4 public static void main(String[] args) { 5 Map<String, List<Integer>> m = new HashMap<>(); 6 m.put("a", new ArrayList() ); ^ 7 System.out.println(m); 8 } 9 }
ダイヤモンド演算子を省くと生の型になり、型チェックが効かなくなります。<> を書くのが基本です。
直し方: new ArrayList() を new ArrayList<>() にします。
パターン4
1 import java.util.HashSet ; ^ 2 3 public class Main { 4 public static void main(String[] args) { 5 Set<Integer> s = new HashSet<>(); 6 s.add(1); 7 System.out.println(s); 8 } 9 }
SetとHashSetは別クラスです。両方使うにはimport java.util.*; でまとめて取り込むのが簡単です。
直し方: HashSet を * にします。
パターン5
1 import java.util.ArrayList; 2 import java.util.List ; ^ 3 4 public class Main { 5 public static void main(String[] args) { 6 List<Integer> list = new ArrayList<>(List.of(3, 1, 2)); 7 Collections.sort(list); 8 System.out.println(list); 9 } 10 }
ArrayListだけimportしても、ListやCollectionsは別途importが必要です。java.util.* でまとめて取り込みます。
直し方: List を * にします。
よくある誤解
IDEの自動importがHashMapだけを足すことがあります。Mapが足りないまま残るので、エラーが消えません。
実務での勘所
Mapを受け口にしてHashMapを実装として使う書き方の実用上のメリットは、後から実装を差し替えやすいことです。順序を保持したくなればLinkedHashMapに、キー順にソートしたくなればTreeMapに、右辺を書き換えるだけで済みます。変数の型をMapのままにしておけば、その変数を使っている残りのコードは一切書き換える必要がありません。最初からHashMap型の変数として書いてしまうと、後からこの種の差し替えをするときに使用箇所すべての型を書き換える手間が発生します。