INTRODUCTION
「List<Person>の<Person>って何?」
前の記事で、 PersonオブジェクトをListに入れるコードを 紹介しました。
List<Person> persons =
new ArrayList<>();
ここで気になるのが、
Listなのは分かる。
でも、なぜ<Person>が付いているの?
という部分です。
実はこの
<Person>
が、
「このListには何を入れるのか」
を表しています。
ジェネリクスは「扱う型を指定する」ための仕組み
01
まずは普通のListを考える
Javaでは、
複数のデータをまとめて扱いたいときに
Listを使います。
List names =
new ArrayList();
そして、データを追加できます。
names.add("田中");
names.add("佐藤");
names.add("鈴木");
これで、 複数の名前をListに入れられます。
ところが、 こんなこともできてしまいます。
names.add("田中");
names.add(30);
文字列の中に、 数値まで入っています。
このListには、
「文字列だけ入っている」と
思っていたのに、
実際には数値も入れられてしまう。
そこで登場するのが、 ジェネリクスです。
02
List<String>にすると何が変わる?
Listに
<String>
を付けてみます。
List<String> names =
new ArrayList<>();
こうすると、
このListには
Stringを入れる、
ということが明確になります。
names.add("田中");
names.add("佐藤");
これは問題ありません。
しかし、
names.add(30);
とすると、 コンパイル時にエラーになります。
「何を入れるListなのか」をコード上で決められる。
03
<Person>ならPersonだけを扱う
前の記事で登場した
Personを使ってみます。
class Person {
String name;
}
このPersonをListに入れます。
List<Person> persons =
new ArrayList<>();
すると、
Person person1 = new Person();
Person person2 = new Person();
persons.add(person1);
persons.add(person2);
のように、 Personオブジェクトを Listに入れられます。
一方で、
persons.add("田中");
のようにStringを入れることはできません。
List<Person>
↓
「Personを入れるList」
Person
├─ person1
└─ person2
これが、
List<Person>
の基本的な考え方です。
04
StringやIntegerにも使える
ジェネリクスは、 Person専用の仕組みではありません。
例えば文字列なら、
List<String> names =
new ArrayList<>();
数値なら、
List<Integer> numbers =
new ArrayList<>();
と書けます。
つまり、
Listという同じ仕組みを使いながら、
中で扱う型を変えることができます。
05
では「<T>」は何なのか?
ジェネリクスを調べていると、 今度はこんなコードを見ることがあります。
class Box<T> {
T value;
}
急に
T
が出てきます。
Tって何?
最初は難しく見えますが、 それほど複雑ではありません。
Tは「あとで型を決めます」という目印
例えば、
Box<String>
とすれば、
Tの部分が
Stringになります。
同じように、
Box<Integer>
とすれば、
Tの部分が
Integerになります。
Box<T>
↓
Tはまだ決まっていない
Box<String>
↓
T = String
Box<Integer>
↓
T = Integer
06
自分でジェネリックなクラスを作ってみる
例えば、
何かを一つだけ保持する
Boxを作ってみます。
class Box<T> {
T value;
void set(T value) {
this.value = value;
}
T get() {
return value;
}
}
このBoxは、 何の型を入れるかを 最初には決めていません。
String用に使えば、
Box<String> box =
new Box<>();
box.set("こんにちは");
Integer用に使えば、
Box<Integer> box =
new Box<>();
box.set(100);
のように使えます。
一つのクラスを、 型だけ変えて使い回せるわけです。
「同じ仕組みを、いろいろな型で使いたい」
そんなときに ジェネリクスが役立ちます。
07
業務コードではどう見える?
実際のJava開発では、 自分でジェネリッククラスを作る場面よりも、 すでに用意されているクラスを見ることのほうが 多いかもしれません。
例えば、
List<User> users;
のようなコードです。
これを見たら、
List
↓
複数のデータを扱う
User
↓
その中に入るデータの型
と考えれば、 かなり読みやすくなります。
さらに、
Map<String, Integer> scores;
というコードがあれば、
と読み取れます。
<>の中を見ると、そのデータ構造が何を扱うのか分かる。
08
「型を後から決める」と考える
ジェネリクスを理解するとき、 細かい仕様を全部覚えようとすると 少し難しく感じます。
最初は、
List<String>
→ Stringを扱うList
List<Person>
→ Personを扱うList
Box<T>
→ 型をあとから指定できるBox
と考えておけば十分です。
つまり、
「この仕組みは、どの型を扱うのか?」
という視点で
<>
の中を見るようにします。
これだけでも、 業務コードを読むときの負担がかなり減ります。
FIELD NOTE
「<>」を見たら、まず中身を見る
業務コードを読んでいて、
List<Order> orders
のようなコードが出てきたら、 まず
「このListにはOrderが入るんだな」
と考えます。
例えば、
List<String> names
List<Person> persons
List<Order> orders
と並んでいたら、 それぞれ何を扱っているListなのかが すぐに分かります。
さらに、
Map<String, User> users
と書いてあれば、
String
→ キー
User
→ 値
というように、
<>
の中身から
データの関係を読み取れます。
Javaのコードが複雑に見えるときほど、 いきなり全体を理解しようとせず、
「<>の中には何が入っている?」
と確認してみると、 意外と整理しやすくなります。
SUMMARY
今回のポイント
ジェネリクスは、 Javaでデータの型を安全に扱うために よく使われる仕組みです。
まずは、
List<String>
→ Stringを扱うList
List<Person>
→ Personを扱うList
Map<String, Integer>
→ StringとIntegerの組み合わせ
<T>
→ 型をあとから指定するための仕組み
くらいのイメージから始めれば十分です。
そして、Javaのコードで
「<>の中には何が入っている?」
と考えられるようになると、
Listや
Mapを使ったコードが
かなり読みやすくなってきます。
前の記事で学んだ
クラス → オブジェクト → List<Person>
という流れも、 ここでつながってきます。