Menu
CoddyTech

境界付き型パラメータ

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" を返します

境界付き型パラメーターを使うと、型の安全性を確保しながら境界型の特定のメソッドにアクセスできるため、ジェネリックをより強力にできます。

challenge icon

チャレンジ

簡単

数値型でのみ動作する統計計算機を構築しましょう! 境界付き型パラメータを使用して、計算機が数値のみを処理できるようにしながら、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]
    }
}
quiz icon腕試し

このレッスンには短いクイズがあります。レッスンを始めて解答し、進捗を記録しましょう。

オブジェクト指向プログラミングのすべてのレッスン

自分で練習してみよう: Javaオンラインコンパイラ