境界付き型パラメータ
CoddyのJavaジャーニー「オブジェクト指向プログラミング」セクションの一部。レッスン 56/87。
これまで、ジェネリック型パラメーターは任意の型を受け入れられます。しかし、制限する必要がある場合はどうすればよいでしょうか?
たとえば、数値の合計を計算する method は、文字列を受け取るべきではありません。bounded 型パラメーターを使うと、ジェネリックで使用できる型を制限できます。
型パラメータに上限を設定するには、extendsキーワードを使用します:
class NumberBox<T extends Number> {
private T value;
public NumberBox(T value) {
this.value = value;
}
public double getDoubleValue() {
return value.doubleValue(); // Numberのメソッドを呼び出せます!
}
}
NumberBox<Integer> intBox = new NumberBox<>(42);
NumberBox<Double> doubleBox = new NumberBox<>(3.14);
// NumberBox<String> strBox = new NumberBox<>("Hi"); // コンパイルエラー!型境界 T extends Number は、T が Number またはそのサブクラス(Integer、Double など)でなければならないことを意味します。これにより、型パラメータに対して Number で定義された methods も呼び出せます。
&を使用して複数の境界を指定できます。1つの境界がクラスの場合は、それを最初に指定する必要があります:
class DataProcessor<T extends Number & Comparable<T>> {
public boolean isGreater(T a, T b) {
return a.compareTo(b) > 0;
}
}ここでは、T は Number を拡張し、かつ Comparable を実装する必要があります。上限付き型パラメーターはジェネリックメソッドでも機能します。
public static <T extends Comparable<T>> T findMax(T a, T b) {
return a.compareTo(b) > 0 ? a : b;
}
String result = findMax("apple", "banana"); // "banana" を返します境界付き型パラメーターを使うと、型の安全性を確保しながら境界型の特定のメソッドにアクセスできるため、ジェネリックをより強力にできます。
チャレンジ
簡単数値型でのみ動作する統計計算機を構築しましょう! 境界付き型パラメータを使用して、計算機が数値のみを処理できるようにしながら、Number class の便利な method にアクセスできるようにします。
コードを3つのファイルに分けて整理します。
NumberStats.java: 数値の統計を計算する generic class を Create します。NumberStats<T extends Number>class には、T型の2つの private fields、value1とvalue2を格納します。both の値を accepts する constructor を含めます。both の値の合計をdoubleとして返すgetSum()method を追加します(Number から継承されたdoubleValue()method を使用します)。また、2つの値の average をdoubleとして返すgetAverage()method も追加します。ComparableBox.java: 2つの値のうち larger な方を見つけられる、multiple bounds を持つ generic class を Create します。ComparableBox<T extends Number & Comparable<T>>class には、T型の2つの private fields、firstとsecondを格納します。both の値を accepts する constructor を含めます。compareTo()を使用して larger な値を返すgetMax()method を追加します。また、larger な値をdoubleに converted して返すgetMaxAsDouble()method も追加します。Main.java: bounded generics を実際に動かしてみましょう! 4つの inputs、つまり2つの整数と2つの double を受け取ります。まず、2つの integer の値を使って
NumberStats<Integer>を Create します。sum と average を出力します。Integer sum: [sum] Integer average: [average]次に、2つの double の値を使って
NumberStats<Double>を Create します。sum と average を出力します。Double sum: [sum] Double average: [average]最後に、2つの integer の値を使って
ComparableBox<Integer>を Create し、次を出力します。Max integer: [max] Max as double: [maxAsDouble]
inputs は次の順番で4つ受け取ります。first integer、second integer、first double、second double です。
bound T extends Number によって、generic type に対して doubleValue() を call できることに注目してください。これは unbounded type parameter ではできません。また、ComparableBox の multiple bounds によって、double に converted することと値を比較することの both が可能になります。
自分で試してみよう
import java.util.Scanner;
class Main {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
// 入力を読み取る
int int1 = scanner.nextInt();
int int2 = scanner.nextInt();
double double1 = scanner.nextDouble();
double double2 = scanner.nextDouble();
// TODO: 2つの整数値で NumberStats<Integer> を作成する
// 合計と平均を次の形式で出力する:
// Integer sum: [sum]
// Integer average: [average]
// TODO: 2つの double 値で NumberStats<Double> を作成する
// 合計と平均を次の形式で出力する:
// Double sum: [sum]
// Double average: [average]
// TODO: 2つの整数値で ComparableBox<Integer> を作成する
// 最大値と double としての最大値を次の形式で出力する:
// Max integer: [max]
// Max as double: [maxAsDouble]
}
}
このレッスンには短いクイズがあります。レッスンを始めて解答し、進捗を記録しましょう。
オブジェクト指向プログラミングのすべてのレッスン
4継承
継承の基本 (extends)super キーワードメソッドのオーバーライド (@Override)コンストラクタチェーンObject クラス単一継承と多段階継承なぜ多重継承ができないのかまとめ:従業員の階層構造自分で練習してみよう: Javaオンラインコンパイラ