ジェネリクス(総称型)
型を“引数”のように後から指定できる仕組み。同じコードを複数の型で安全に再利用でき、型安全とコード共通化を両立できます。
- 1.ジェネリクスは型をパラメータ化し、1つの定義を多くの型で使い回す仕組みです。
- 2.`List<T>` のように型引数を渡すと、各要素の型が保たれ型安全とコード共通化を両立できます。
- 3.型ごとにコードを複製する必要がなく、何でも型(Object など)に頼るより安全です。
ジェネリクスとは
ジェネリクス(総称型)は、関数やクラスを特定の型に縛らず書き、使うときに型を「引数」のように渡せる仕組みです。値を受け取る引数があるように、型を受け取る引数があると考えると分かりやすいです。
- 「整数のリスト」「文字列のリスト」を別々に作らず、1つの
List定義で済ませられます。 - 取り出した要素の型が保たれるため、コンパイラが誤った使い方を弾けます。
- 型引数は慣例で
T(Type)やE(Element)などの記号で書きます。
つまり「同じロジックを、いろいろな型で安全に再利用する」ための道具です。
なぜ必要か:3つの選択肢
ジェネリクスが無い世界では、複数の型に対応する方法が限られ、どれも欠点があります。
| 方法 | 内容 | 問題点 |
|---|---|---|
| 型ごとに複製 | IntList と StringList を別々に書く | コード重複。修正漏れの温床 |
| 何でも型を使う | 中身を Object / any で持つ | 取り出し時に型が消え、安全でない |
| ジェネリクス | 型を引数化し1つで共通化 | 型安全と再利用を両立 |
「何でも型」は一見柔軟ですが、取り出すたびにキャスト(型変換)が必要で、間違えても実行時まで気づけません。ジェネリクスはこの欠点を解消します。
基本的な書き方
List<T> の T が型引数です。List<int> と書けば「int のリスト」として振る舞い、add には int しか入れられず、取り出した値も int として扱えます。
// TypeScript:型引数 T を取る関数
function first<T>(items: T[]): T {
return items[0];
}
const n = first<number>([1, 2, 3]); // n の型は number
const s = first(["a", "b"]); // 推論で T = string
// Java:型引数を持つクラスの利用
List<String> names = new ArrayList<>();
names.add("Alice");
String head = names.get(0); // キャスト不要・String と分かっている
多くの言語では、渡した値から型引数を自動推論してくれるため、毎回 <...> を明示する必要はありません。
制約(境界)を付ける
「どんな型でもよい」ではなく「ある条件を満たす型に限る」としたい場面があります。これを**型制約(境界)**と呼びます。たとえば「比較できる型だけ」「特定のインターフェースを実装した型だけ」と縛れます。
interface HasLength {
length: number;
}
// length を持つ型に限定する
function longest<T extends HasLength>(a: T, b: T): T {
return a.length >= b.length ? a : b;
}
longest("ab", "abc"); // 文字列も配列も length を持つので OK
制約を付けると、その型が持つメンバ(上記なら length)を安全に使えます。制約なしの型引数は「何が来るか不明」なので、限定された操作しかできません。
型引数は明示しなくても、引数の値から推論されることが多いです。first([1, 2, 3]) のように書けば T は自動で決まります。読みづらいときや推論が効かないときだけ <number> を明示しましょう。
注意点
便利な一方で、言語ごとの実装の違いに起因する落とし穴があります。
Java は 型消去(type erasure) を採用し、実行時には型引数の情報が消えます。そのため実行時に「T が何か」を直接調べる処理は書けません。一方 C# は実行時にも型情報を保持します。ジェネリクスは万能の動的機構ではなく、主にコンパイル時の安全装置だと捉えると誤解を避けられます。
過度に多くの型引数を持たせると、シグネチャが読みにくくなります。必要な範囲にとどめるのがコツです。
まとめ
ジェネリクスは型を引数化して、同じコードを多くの型で安全に再利用する仕組みです。List<T> のように型を渡すことで、要素の型が保たれ、型安全とコード共通化を両立できます。型ごとの複製や「何でも型」に頼らず、必要なら制約で対象を絞る。これがジェネリクスを使いこなす基本です。
プログラミング Article
ジェネリクス(総称型)を実務で読む
TL;DRは入口です。実際に選ぶ・使う段階では、何を解決するか、何と比較するか、導入後にどこで詰まるかまで見る必要があります。
解決すること
ジェネリクス
比較で見る軸
難易度: intermediate / カテゴリ: プログラミング / タグ数: 3
導入後に効く点
`List<T>` のように型引数を渡すと、各要素の型が保たれ型安全とコード共通化を両立できます。
先に潰すリスク
用語だけ覚えても、設計・実装・運用でどこに効くかを確認しないと判断を誤る。
- 難易度
- intermediate
- カテゴリ
- プログラミング
- タグ数
- 3
判断チェックリスト
- 自社の用途が「ジェネリクス / 型システム」に近いか確認する。
- 強みである「ジェネリクスは型をパラメータ化し、1つの定義を多くの型で使い回す仕組みです。」が本当に評価軸になるか確認する。
- 注意点の「用語だけ覚えても、設計・実装・運用でどこに効くかを確認しないと判断を誤る。」を運用で吸収できるか確認する。
- 公開値や仕様値は、対象プラン・対象機種・対象リージョンまで確認する。
- 既存システム、ID、ネットワーク、監視、バックアップとの接続方法を先に洗い出す。
- 小さく試してから、本番移行、権限設計、障害時手順、コスト監視を決める。