---
title: "ジェネリクスとは？型パラメータの仕組みと7言語の書き方・実装方式の違い"
url: "https://www.issoh.co.jp/tech/details/3535/"
published: 2024-09-09
updated: 2026-10-28
categories: ["プログラミング言語"]
publisher: "株式会社一創"
---

# ジェネリクスとは？型パラメータの仕組みと7言語の書き方・実装方式の違い

ジェネリクス（generics、総称型）とは、クラスや関数が扱う型を決め打ちせず、「型そのもの」を引数として受け取れるようにする言語機能です。`List<String>`の`String`のように、使う側が型を指定すると、1つの実装を複数の型で使い回しながら、型の誤りをコンパイル時（または型チェック時）に見つけられます。この記事では、同じ関数をJava・C#・TypeScript・Go・Rust・Python・PHPの7言語で書いて実行した結果をもとに、書き方と実装方式（型消去・具象化・単相化）の違い、使うべき場面を整理します。

## まとめ：ジェネリクスの要点

- ジェネリクスは**型を引数にする仕組み**。`T`などの型パラメータで書いておき、使う側が`String`や`int`を型引数として渡す。
- 解決するのは、`any`や`Object`で汎用化したときに**型の誤りが実行時まで見つからない**問題。ジェネリクスは型の関係を検査できるが、どこまで止まるかは言語・型の指定・型チェッカーの設定で変わる。
- 実装方式は3系統。**Java・TypeScriptは型消去**（実行時に型引数が消える）、**C#は具象化**（実行時も型引数が残る）、**Rustは単相化**（型ごとに機械語を生成）。Goは単相化と辞書の併用。
- Pythonの型ヒントとPHPDocの`@template`は**実行時に何も強制しない**。mypyやPHPStanを回して初めて誤りが見つかる。
- 型パラメータが入力と出力の型を結び付けていない関数（1か所にしか使われない）や、メソッドを呼ぶだけの関数はジェネリクスにしない。インターフェースや具体型で書いたほうが読みやすい。

## ジェネリクスの意味：型を引数として受け取る仕組み

英語のgenericは「総称的な・特定の銘柄に限らない」という意味で、ジェネリック医薬品のジェネリックと同じ語です。プログラミングでは「特定の型に限らない」コードを指します。

関数の引数が「値」を外から受け取るのに対し、ジェネリクスは「型」を外から受け取ります。`List<T>`の`T`に何を入れるかは使う側が決めます。

### 型パラメータ・型引数・ジェネリック型の用語

似た用語が混ざりやすいので、Javaの書き方で整理します。

| 用語                 | 指すもの                  | 例                         |
| ------------------ | --------------------- | ------------------------- |
| 型パラメータ（型変数）        | 宣言側に置く型の仮引数           | `class Box<T>`の`T`        |
| 型引数                | 使う側が渡す具体的な型           | `Box<String>`の`String`    |
| ジェネリック型（ジェネリッククラス） | 型パラメータを持つクラス・インターフェース | `Box<T>`、`List<E>`        |
| パラメータ化された型         | 型引数を当てはめた後の型          | `List<String>`            |
| ジェネリックメソッド         | メソッド単位で型パラメータを持つもの    | `<T> T first(List<T> xs)` |
| 境界（制約）             | 型パラメータに入れられる型の条件      | `T extends Comparable<T>` |

### ジェネリックプログラミング・C++テンプレートとの関係

ジェネリックプログラミングは、型に依存しない形でアルゴリズムとデータ構造を書くという設計の考え方です。名前を広めたのはDavid MusserとAlexander StepanovがISSAC 1988で発表した論文「Generic Programming」で、Stepanovは後にC++の標準テンプレートライブラリ（STL）を設計しました。ジェネリクスは、この考え方を言語機能として実現する手段の1つです。

C++ではほぼ同じ役割を**テンプレート**が担います（C++98のISO/IEC 14882:1998第14章で標準化）。テンプレートは型ごとにコードを展開し、型のほかに整数などの値も非型テンプレート引数として取れるため、JavaやC#のジェネリクスより表現力は広い一方、エラーメッセージが長くなりがちです。

## ジェネリクスが解決する問題：anyやObjectとの型情報の違い

ジェネリクスが無い時代、どんな型でも受け取る汎用コードは、すべてのクラスの親（Javaの`Object`）や型検査をやめる型（TypeScriptの`any`）などで書かれていました。共通の操作だけが必要なら、インターフェースを使う方法もあります。`any`はその値への型検査をまるごと止めます。Javaの`Object`はそのまま`String`の変数には代入できないものの、`(String)`のようなキャストを書けばコンパイルが通り、型が違えば実行時に`ClassCastException`になります。どちらも、戻り値の本当の型を確かめる仕事が実行時へ持ち越される点は同じです。TypeScript 7.0.2で確かめたコードです。

```
function largerAny(a: any, b: any): any {
  return a >= b ? a : b;
}
const x = largerAny(3, 7);
x.toUpperCase();

function larger<T extends number | string>(a: T, b: T): T {
  return a >= b ? a : b;
}
const n = larger(3, 7);
n.toUpperCase();
larger(3, "a");
```

`tsc --strict --noEmit`の結果、`any`版の`x.toUpperCase()`は素通りし、実行して初めて`TypeError: x.toUpperCase is not a function`になります。ジェネリクス版は次の2件をコンパイル時に止めました。ただし、`larger<number | string>(3, "a")`のように型引数を共用体型で明示すると通るため、実行時の型が必ず一致するという保証ではありません。

```
larger.ts(11,3): error TS2339: Property 'toUpperCase' does not exist on type '3 | 7'.
larger.ts(12,11): error TS2345: Argument of type '"a"' is not assignable to parameter of type '3'.
```

ポイントは、ジェネリクス版の戻り値の型が「呼び出しごとに」決まることです。`larger(3, 7)`の戻り値は数値（リテラル型の`3 | 7`）、`larger("apple", "banana")`なら文字列として扱われます。`any`は型の情報を捨て、ジェネリクスは型の情報を呼び出し元から戻り値まで運びます。TypeScriptの型推論と型注釈の書き分けは[TypeScriptの型の解説](/tech/details/2815/)で扱っています。

## 同じ関数を7言語で書く：2つの値の大きい方を返すlarger

「2つの値を比べて大きい方を返す」関数`larger`を7言語で書き、`larger(3, 7)`・`larger("apple", "banana")`・誤った呼び出しの`larger(3, "a")`を試しました。比べられない型を渡されたら困るので、どの言語でも型パラメータに「比較できる型に限る」という境界（制約）を付けています。書き方の差を先に表で示します。

| 言語         | 型パラメータ        | 比較できる型への制約                   | 誤った呼び出しを止めるもの    |
| ---------- | ------------- | ---------------------------- | ---------------- |
| Java       | `<T>`         | `T extends Comparable<T>`    | javac            |
| C#         | `<T>`         | `where T : IComparable<T>`   | C#コンパイラ          |
| TypeScript | `<T>`         | `T extends number \| string` | tsc              |
| Go         | `[T]`         | `cmp.Ordered`                | go build         |
| Rust       | `<T>`         | `T: PartialOrd`              | rustc            |
| Python     | `[T]`         | `T: (int, str)`              | mypy等（実行時は無し）    |
| PHP        | `@template T` | `@template T of ...`         | PHPStan等（実行時は無し） |

表の右端が、言語ごとの一番大きな違いです。Pythonの型ヒントやPHPDocの型パラメータは、実行時の型検査を強制しません。宣言との不一致を実行前に検出するには型チェッカーが必要ですが、比較などの操作自体が実行時エラーになる場合はあります。

### Java・C#：extendsとwhereによる型の境界

Javaは`extends`、C#は`where`句で境界を書きます。どちらも`Comparable`／`IComparable`を実装した型に限ることで、関数の中で`compareTo`／`CompareTo`を呼べるようになります。

以下はJava用とC#用の別々のプログラムです。JavaはLarger.javaとして保存して`java Larger.java`、C#はコンソールプロジェクトのProgram.csに保存して`dotnet run`で実行します。

```
public class Larger {
    static <T extends Comparable<T>> T larger(T a, T b) {
        return a.compareTo(b) >= 0 ? a : b;
    }

    public static void main(String[] args) {
        System.out.println(larger(3, 7));
        System.out.println(larger("apple", "banana"));
    }
}
```

```
static T Larger<T>(T a, T b) where T : IComparable<T> => a.CompareTo(b) >= 0 ? a : b;

Console.WriteLine(Larger(3, 7));
Console.WriteLine(Larger("apple", "banana"));
```

Javaの`larger`、C#の`Larger`とも、`(3, 7)`では`7`、`("apple", "banana")`では`banana`を返しました。Javaで`larger(3, "a")`と書くと、javacは`reason: inference variable T has incompatible bounds`（型変数Tの境界が両立しない）で止まります。Javaのジェネリッククラスの定義、ワイルドカード、型消去による制限の詳細は[Javaのジェネリクスの解説記事](/tech/details/3252/)にまとめています。

### Go：角括弧の型パラメータとインターフェースによる制約

Goは2022年3月15日公開のGo 1.18でジェネリクスを導入しました（[Go公式ブログ「Go 1.18 is released!」](https://go.dev/blog/go1.18)）。型パラメータは角括弧で書き、制約はインターフェースで表します。標準パッケージ`cmp`の`cmp.Ordered`は、`<`や`>=`で比較できる整数・浮動小数点数・文字列の型をまとめた制約です。`cmp`パッケージ自体はGo 1.21で追加されたため、次のコードはGo 1.21以降で動きます（[Go 1.21リリースノート](https://go.dev/doc/go1.21)）。

```
package main

import (
	"cmp"
	"fmt"
)

func Larger[T cmp.Ordered](a, b T) T {
	if a >= b {
		return a
	}
	return b
}

func main() {
	fmt.Println(Larger(3, 7))
	fmt.Println(Larger("apple", "banana"))
}
```

Go 1.27.2で`Larger(3, "a")`をビルドすると、`mismatched types untyped int and untyped string (cannot infer T)`で止まりました。

Go 1.26まではメソッドに独自の型パラメータを付けられませんでしたが、2026年8月のGo 1.27で`func (l List[T]) Map[U any](f func(T) U) List[U]`のようなジェネリックメソッドが書けるようになりました（Go 1.27.2で実行を確認）。ただし、インターフェースのメソッドは型パラメータを持てず、ジェネリックメソッドでインターフェースを実装することもできません（[Go 1.27リリースノート](https://go.dev/doc/go1.27)）。Goの導入と基本文法は[Go言語の実装入門](/tech/details/17483/)を参照してください。

### Rust：PartialOrdのトレイト境界

Rustでは「`T`は`PartialOrd`トレイトを実装している」というトレイト境界を`T: PartialOrd`と書きます。

```
fn larger<T: PartialOrd>(a: T, b: T) -> T {
    if a >= b { a } else { b }
}

fn main() {
    println!("{}", larger(3, 7));
    println!("{}", larger("apple", "banana"));
}
```

rustc 1.99.1で`larger(3, "a")`を渡すと`error[E0308]: mismatched types`になり、「1つ目の引数が整数なので、2つ目も同じ整数型である必要がある」と指摘されました。なお`PartialOrd`は「比べられない組み合わせがありうる」比較で、浮動小数点数の`NaN`はどの値とも大小が決まりません。汎用の最大値関数にするなら、全順序を保証する`Ord`に絞るほうが安全です（[PartialOrdのドキュメント](https://doc.rust-lang.org/core/cmp/trait.PartialOrd.html)）。Rustの所有権や基本文法は[Rust入門](/tech/details/15373/)で解説しています。

### Python：PEP 695の型パラメータ構文とmypyによる検査

Pythonは3.5で入った`typing.TypeVar`で型パラメータを表してきましたが、3.12からは関数・クラスの定義に直接書けます（[PEP 695](https://peps.python.org/pep-0695/)）。`T: (int, str)`は「Tは`int`か`str`のどちらか」という制約です。

```
def larger[T: (int, str)](a: T, b: T) -> T:
    return a if a >= b else b

print(larger(3, 7))
print(larger("apple", "banana"))
larger(3, "a")
```

mypy 2.4.0は最終行を`Value of type variable "T" of "larger" cannot be "object"`と指摘しました。一方、Python 3.14.6でそのまま実行すると、`7`と`banana`を表示したあと、最終行で`TypeError: '>=' not supported between instances of 'int' and 'str'`になります。これは型パラメータの検査ではなく、`>=`の比較そのものの失敗です。`b: Box[int] = Box("文字列")`のような宣言違反は、実行時には何事もなく通りました。Pythonでジェネリクスを使うなら、mypyやPyrightをCIに組み込むところまでが1セットです。

### PHP：PHPDocの@templateとPHPStanによる検査

PHP 8.5にもジェネリクスの構文はありません。PHP 8.5.8で`class Box<T> {}`と書くと`syntax error, unexpected token "<"`になります。実務では、PHPDocコメントの`@template`で型パラメータを宣言し、PHPStanやPsalmといった静的解析ツールに検査させます（[PHPStan「Generics in PHP using PHPDocs」](https://phpstan.org/blog/generics-in-php-using-phpdocs)）。

```
<?php

/**
 * @template T of int|string
 * @param T $a
 * @param T $b
 * @return T
 */
function larger(int|string $a, int|string $b): int|string
{
    return $a >= $b ? $a : $b;
}

echo larger(3, 7), PHP_EOL;
echo larger('apple', 'banana'), PHP_EOL;
echo larger(3, 'a'), PHP_EOL;
```

PHP 8.5.8で実行すると`7`、`banana`に続き、3行目も止まらずに`a`を出力しました。しかもPHPStan 2.3.3はレベル9でもこの呼び出しを指摘しませんでした。`\PHPStan\dumpType()`で確かめると、`T`は`3|'a'`という和集合の型に推論されており、「2つの引数が同じ型であること」までは縛れていません。

PHPStanが型の不一致を検出する例として、要素の型を固定したコレクションがあります。`@template T`付きの`Box`クラスで、`Box<int>`を受け取る関数に`new Box('21')`を渡すと、レベル9で`Parameter #1 $box of function double expects Box<int>, Box<string> given.`と指摘されました（実行時はエラーになりません）。PHPStanの導入とレベルの選び方は[PHPStanのレベルと導入の解説](/tech/details/12338/)にあります。

## 実装方式の違い：型消去・具象化・単相化

コンパイル後にジェネリクスをどう扱うかは言語で分かれ、それが「実行時に型引数を調べられるか」「型ごとにコードが増えるか」を決めます。

| 方式     | 言語              | 実行時の型引数       | 主な帰結                   |
| ------ | --------------- | ------------- | ---------------------- |
| 型消去    | Java、TypeScript | 残らない          | `new T()`や型引数での分岐ができない |
| 具象化    | C#（.NET）        | 残る            | `typeof(T)`で型を取得できる    |
| 単相化    | Rust、C++        | 型ごとに別の関数      | 具体型ごとに実体化しコード量が増えうる    |
| 単相化＋辞書 | Go              | 辞書で実行時型記述子を渡す | 同じGC shapeの型で実装を共有     |
| 注釈のみ   | Python、PHP      | 検査されない        | 型チェッカーが無ければ効果なし        |

型消去と具象化の差は、同じクラスに別の型引数を当てたときの比較ではっきり出ます。

```
// Java（JDK 25）：true
System.out.println(new ArrayList<String>().getClass() == new ArrayList<Integer>().getClass());

// C#（.NET 10）：False、Int32
Console.WriteLine(new List<string>().GetType() == new List<int>().GetType());
Console.WriteLine(NameOf<int>());
static string NameOf<T>() => typeof(T).Name;
```

Javaでは`ArrayList<String>`と`ArrayList<Integer>`が実行時には同じ`ArrayList`クラスで、比較は`true`でした。C#は`False`を返し、関数の中で`typeof(T)`から`Int32`という型名まで取れました。.NETのジェネリック型は実行時にも型引数の情報を持ち、リフレクションで確認できます（[Microsoft Learn「Generics in the runtime」](https://learn.microsoft.com/en-us/dotnet/csharp/programming-guide/generics/generics-in-the-run-time)）。型ごとの分岐やインスタンス生成は、C#なら素直に書け、Javaでは`Class<T>`を引数で渡すなどの回り道が要ります。実行時に型情報を扱う仕組みは[リフレクションの言語横断の解説](/tech/details/15262/)で掘り下げています。

Rustの単相化は、`larger::<i32>`と`larger::<&str>`のように使われた型ごとに専用の関数を生成する方式で、型を抽象化したことによる実行時コストはかかりません（[The Rust Programming Language 10.1節](https://doc.rust-lang.org/book/ch10-01-syntax.html)）。その代わり、使う型が増えるとコンパイル時間とバイナリサイズが増えることがあります（実際の増え方は最適化やインライン化で変わります）。この考え方は[ゼロコスト抽象化の解説](/tech/details/4440/)で詳しく扱っています。Goは全面的な単相化を避け、Go 1.18で、基底型が同じ型どうし、またはポインタ型どうしを同じ「GC shape」として1つの実装を共有し、具体的な型引数は「辞書」（実行時型記述子の一覧）で渡す方式（GC shape stenciling）を採りました（[Go 1.18の実装設計文書](https://go.googlesource.com/proposal/+/refs/heads/master/design/generics-implementation-dictionaries-go1.18.md)）。

## ジェネリクスの歴史：MLの型多相からGo 1.27まで

ジェネリクスの源流は、Robin Milnerが1978年の論文「A Theory of Type Polymorphism in Programming」で報告した、MLの多相型の型検査です。研究言語で生まれたこの考え方が、40年以上かけて主流の言語に広がりました。

| 年     | 言語・出来事                               |
| ----- | ------------------------------------ |
| 1978年 | ML：Milnerが多相型の型検査を論文で報告              |
| 1983年 | Ada 83：generic unitsを標準化             |
| 1988年 | Musser・Stepanov「Generic Programming」 |
| 1998年 | C++98：テンプレートとSTLを標準化                 |
| 2004年 | J2SE 5.0（9月30日）：JSR 14のジェネリクス        |
| 2005年 | C# 2.0（11月）：具象化されたジェネリクス             |
| 2013年 | TypeScript 0.9（6月18日）：ジェネリクスを追加      |
| 2014年 | Swift 1.0：ジェネリクスを初版から搭載              |
| 2015年 | Rust 1.0／Python 3.5（PEP 484の型ヒント）    |
| 2022年 | Go 1.18（3月15日）：ジェネリクスを導入             |
| 2023年 | Python 3.12：PEP 695の型パラメータ構文         |
| 2024年 | Python 3.13：PEP 696の型パラメータ既定値        |
| 2026年 | Go 1.27：ジェネリックメソッド                   |

導入が遅れた理由は言語ごとに違います。Goの公式FAQは、当初は単純さを優先してジェネリクスを見送り、型システムと実行時の複雑さに見合う設計に至るまで時間がかかったと説明しています（[Go FAQ「Why was Go initially released without generic types?」](https://go.dev/doc/faq)）。PHP Foundationは2024年8月の報告で、実行時に型を検査するジェネリクスの最大の難所を型推論（PHPのコンパイラはファイル単位でしかコードを見られない）とし、構文だけを入れて検査を静的解析ツールに任せる案も並べたうえで、どれが実現可能かは調査中だと結んでいます（[PHP Foundation「State of Generics and Collections」](https://thephp.foundation/blog/2024/08/19/state-of-generics-and-collections/)）。

## 変性：List<String>とList<Object>の代入関係

`String`は`Object`の一種なのに、Javaでは`List<String>`を`List<Object>`の変数に代入できません。JDK 25のjavacは`incompatible types: List<String> cannot be converted to List<Object>`で止めます。代入できると、`List<Object>`経由で整数を追加でき、元のリストに整数が混ざるからです。実際、この代入を許すJavaの配列では、`Object[] arr = new String[1]; arr[0] = 1;`がコンパイルを通り、実行時に`java.lang.ArrayStoreException`で落ちました。

取り出すだけなら安全なので、各言語は共変（`out`）・反変（`in`）を指定する方法を持っています。Javaは使う側で`List<? extends T>`／`List<? super T>`と書き、C#とKotlinは宣言側で型パラメータに`out`／`in`を付けます。ただし、C#で指定できるのはインターフェースとデリゲートで、変性による変換は参照型の型引数に限られます。C#の`IEnumerable<out T>`は共変なので`IEnumerable<object> objs = new List<string>();`は通り、`List<object>`への代入だけがCS0029で止まりました。TypeScriptは4.7から`in`／`out`の変性注釈を書けます（[TypeScript 4.7のリリースノート](https://www.typescriptlang.org/docs/handbook/release-notes/typescript-4-7.html)）。値を取り出すだけなら共変、入れるだけなら反変、両方なら不変、が判断の軸です。

## ジェネリクスを使うべき場面・避けるべき場面

**使うべき場面**は、要素の型だけが違う同じ処理を書くときです。リスト・マップ・キャッシュ・結果型（成功値かエラーか）のようなコンテナ、ソートや検索のように要素の型に依存しないアルゴリズム、APIクライアントの「レスポンスの型を呼び出し側が指定する」関数がこれに当たります。Go公式ブログの「When To Use Generics」（2022年4月、Ian Lance Taylor）も、スライス・マップ・チャネルといった言語組み込みのコンテナを扱う関数と、汎用のデータ構造を代表例に挙げています（[The Go Blog「When To Use Generics」](https://go.dev/blog/when-generics)）。

**避けるべき場面**は3つあります。

- **型パラメータが1回しか出てこない**：`function greet<T extends string>(s: T): void`の`T`は引数にしか登場せず、`string`と書くのと変わりません。TypeScriptハンドブックは「型パラメータは2回現れるべき」という指針を示しています（[TypeScript Handbook「Guidelines for Writing Good Generic Functions」](https://www.typescriptlang.org/docs/handbook/2/functions.html#guidelines-for-writing-good-generic-functions)）。
- **メソッドを呼ぶだけ**：引数の`Write`メソッドを呼ぶだけの関数なら、Goでは`io.Writer`のようなインターフェース型の引数で足ります。同ブログも「値のメソッドを呼ぶだけならインターフェース型を使う」と明言しています。インターフェースの考え方は[インターフェースの意味と種類の解説](/tech/details/17395/)を参照してください。
- **今は1〜2種類の型でしか使わない**：将来の汎用化を見込んで最初から型パラメータを付けると、読み手は不要な抽象を追うことになります。Go公式ブログは「まったく同じコードを何度も書こうとしていると気づくまで型パラメータは避ける」と書いており、置き換えはそれからで間に合います。

既存システムが`any`や`Object`のキャストだらけになっている場合は、部分的なジェネリクス化より設計の作り直しが早いこともあります。型設計から見直す開発は[フルスクラッチ開発](https://www.issoh.co.jp/service/system/fullscratch/)として相談を受けています。

## よくある質問

### ジェネリクスの英語と読み方は？

英語ではgenerics（ジェネリクス）で、形容詞のgenericは「総称的な」という意味です。日本語では総称型とも訳され、「ジェネリックス」と表記されることもあります。型パラメータを持つ型は、英語ではgeneric type（ジェネリック型）と呼びます。

### <T>のTは何の略ですか？

TはType（型）の頭文字です。型パラメータ名は1文字の大文字にする慣習があり、Oracleのチュートリアルは`E`（Element＝要素）、`K`（Key）、`N`（Number）、`T`（Type）、`V`（Value）を挙げています（[Oracle「Generic Types」](https://docs.oracle.com/javase/tutorial/java/generics/types.html)）。2つ目以降の型パラメータには`S`、`U`、`V`を順に使います。名前は文法上の決まりではないので、`TResult`のような長い名前も書けます。

### ジェネリック関数をアロー関数で書くには？

TypeScriptでは`const id = <T>(x: T): T => x;`と書けます。ただし、Reactなどの`.tsx`ファイルでは`<T>`がJSXの開始タグと解釈され、TypeScript 7.0.2では`error TS17008: JSX element 'T' has no corresponding closing tag.`になりました。`.tsx`では`<T,>`と末尾にカンマを付けるか、`<T extends unknown>`と書くとコンパイルが通ります。

### ジェネリクスを使うと実行速度は落ちますか？

実行速度への影響は言語の実装と扱う型によって異なり、具体型で書いた場合との差は一律には言えません。Rustは単相化で型の抽象化コストを消します。Javaでは型消去に伴うキャストに加え、プリミティブ型専用の処理との比較ではボクシング・アンボクシングのコストも検討が必要です。多くの型に展開するRust・C++では、速度よりビルド時間とバイナリサイズを測って判断してください。

### ジェネリクスと継承（ポリモーフィズム）はどう使い分けますか？

継承は「型は違っても同じ操作ができる」ことを表し、ジェネリクスは「同じ処理をどの型にも適用できる」ことを表します。図形ごとに面積の計算方法が違うなら継承やインターフェース、要素の型によらず同じ順に並べ替えるならジェネリクスです。両者は組み合わせて使うのが普通で、`T extends Comparable<T>`のような境界は、ジェネリクスにインターフェースの条件を付けたものです。

## 関連記事

- [Javaのジェネリクスとは｜型パラメータTの書き方・extendsとsuperの使い分け・型消去の制約をJDK 25で実測](/tech/details/3252/)
- [リフレクションとは？実行時にクラス情報を操る仕組みを言語横断で解説](/tech/details/15262/)
- [インターフェースとは？意味と種類・コードと通信の契約を実装目線で解説](/tech/details/17395/)
- [ゼロコスト抽象化とは？Rustで抽象度と実行速度を両立する仕組み](/tech/details/4440/)
- [TypeScriptとは？型・型推論・型注釈の書き分けとJavaScriptとの違い【2026年版】](/tech/details/2815/)

---

出典: [ジェネリクスとは？型パラメータの仕組みと7言語の書き方・実装方式の違い](<https://www.issoh.co.jp/tech/details/3535/>)（株式会社一創）
