JavaGold資格取得に向けた勉強記第14回は「ジェネリクス」についてです。
ここではJavaGold資格取得に向けて勉強している駆け出しプログラマーの私がアウトプットの意味も込めて、「ジェネリクス」について紹介させていただきます。
JavaGold資格取得に向けて勉強されている方や、プログラマー初心者の方にとって参考になれば幸いです!
ジェネリクス
ジェネリクスは、Javaにおいて異なる型のオブジェクトを扱う際に、型安全性を確保するための仕組みです。ジェネリクスを使用することで、コードの再利用性が向上し、型に関するコンパイル時のエラーが減少します。
Java SE 7では、変数宣言時の型パラメータを使って、コンストラクタ使用時の方パラメータを推論する「型推論」という機能が盛り込まれました。型推論は、ダイヤモンド演算子「< >」を記述するだけで実現できます。
List<String> list = new ArrayList<>();
ジェネリクスの型推論は、変数への代入など型が明確でなければ使えません。型推論が使えるのは以下のような箇所です。
- 変数への代入
- メソッドの戻り値
- メソッドの呼び出しの引数
import java.util.ArrayList;
import java.util.List;
public class javaGold {
public static void main(String[] args) {
// 変数への代入
List<String> list = new ArrayList<>();
// メソッド呼び出しの引数
execute(new ArrayList<>());
}
private static List<String> test() {
// メソッドの戻り値
return new ArrayList<>();
}
private static void execute(List<String> list) {
// do something
}
}
制約つきの型パラメータ
制約付きの型パラメータとは、ジェネリクスを宣言するときにextendsを使って、型パラメータとして使用できるクラスを制限する仕組みです。
例えば、<T extends A>
のように制約付きの型パラメータを使うことで、型パラメータとして使用できるクラスをAクラスもしくはAクラスのサブクラスに制限することができ、互換性が保証されます。
public class A {
public void hello() {
System.out.println("A");
}
}
public class Test<T extends A> {
public void execute(T t) {
t.hello();
}
}
変性
変性(Variance)は、ジェネリクスにおいて型パラメータがサブタイプ関係をどのように保つかに関する概念です。
Javaにおいては、主に共変性(covariance)、反変性(contravariance)、非変性(invariance)の3つの変性があります。これらはワイルドカードとともに使用され、ジェネリック型をより柔軟に取り扱うことを可能にします。
共変性
共変性は、サブタイプ関係が保たれる性質です。言い換えれば、型A
が型B
のサブタイプであれば、ジェネリック型SomeType<A>
はSomeType<B>
のサブタイプとなります。
class Fruit { /* ... */ }
class Apple extends Fruit { /* ... }
List<? extends Fruit> fruits = new ArrayList<Apple>(); // 共変性
この例では、Apple
はFruit
のサブタイプであるため、List<Apple>
はList<? extends Fruit>
のサブタイプとなります。
反変性
反変性は、型の逆のサブタイプ関係が保たれる性質です。言い換えれば、型A
が型B
のサブタイプであれば、ジェネリック型SomeType<B>
はSomeType<A>
のサブタイプとなります。
class Box<T> {
private T value;
public void setValue(T value) {
this.value = value;
}
}
Box<? super Apple> box = new Box<Fruit>(); // 反変性
この例では、Box<? super Apple>
はBox<Fruit>
のサブタイプとなります。super
ワイルドカードは反変性を表します。
非変性
非変性は、型のサブタイプ関係を持たない性質です。すなわち、型A
が型B
のサブタイプであっても、ジェネリック型SomeType<A>
とSomeType<B>
は直接的なサブタイプ関係にはありません。
List<Fruit> fruits = new ArrayList<Apple>(); // 非変性
この例では、List<Fruit>
とList<Apple>
は直接的なサブタイプ関係にありません。型安全性が保たれる一方で、柔軟性が制限される特性を持っています。
ワイルドカード
ワイルドカードは、Javaにおいてジェネリクスを使用する際に、型の柔軟性を提供するための機能です。ワイルドカードは主にメソッドの引数や戻り値、またはクラスやインターフェースの型パラメータで使用されます。
ワイルドカードには主に3つの種類があります。
非境界ワイルドカード「?」
?
は未知の型を表します。具体的な型が不確定であり、ワイルドカードを使用した部分には任意の型が指定できます。
List<?> list = new ArrayList<Integer>(); // 非境界ワイルドカード
非境界ワイルドカードには次の特徴があります。
- メソッドの戻り値型はObject型になる
- メソッドの引数にはnullリテラルしか渡せない
上限境界ワイルドカード「? extends T」
? extends T
は、型T
またはT
のサブタイプを表します。主に読み取り専用のコンテナに使用されます。
List<? extends Number> numbers = new ArrayList<Integer>(); // 上限境界ワイルドカード
上限境界ワイルドカードを使用することで、非境界ワイルドカードの特徴である「メソッドの戻り値型はObject型になる」を「任意の型にする」ことができるようになります。
下限境界ワイルドカード「? super T」
? super T
は、型T
またはT
のスーパータイプを表します。主に書き込み可能なコンテナに使用されます。
List<? super Integer> integerList = new ArrayList<Number>(); // 下限境界ワイルドカード
下限境界ワイルドカードを使用することで、非境界ワイルドカードの特徴である「メソッドの引数にはnullリテラルしか渡せない」を「任意の型にする」ことができるようになります。
メソッド内の目的が、戻り値を戻すことを目的とした「Producer(提供者)」であればextendsを、引数を受け取って利用することを目的とした「Consumer(消費者)」であればsuperを使います。この原則はProducer-Extends and Consumer-Superを略して「PRCS」とも呼ばれます。
最後に
ジェネリクスは、Javaプログラミングにおいて型安全性とコードの再利用性を向上させる強力な機能です。型パラメータを使用して異なる型に対して一般的なコードを適用することで、プログラムの柔軟性が向上し、コードの品質が向上します。ワイルドカードを組み合わせることで、より柔軟で拡張可能なジェネリックなコードを書くことができます。