境界型パラメータ
T のままでは、数として扱えない
<T> と書けばどんな型でも受け取れます。ただし、受け取ったあとにできることは驚くほど少なくなります。
Java
public static <T> double half(T value) {
return value.doubleValue() / 2; // コンパイルエラー
}コンパイラから見ると T は何になるか分からない型なので、どんな型にもある toString や equals くらいしか呼べません。doubleValue() は java.lang.Number が持つメソッドで、T が Number の仲間だと保証されていない以上、呼び出しは弾かれます。何でも受け取れることと、受け取ったものを使えることは別なのです。
extends で「ここから下」に絞る
型パラメータには上限を付けられます。<T extends Number> と書けば、T は Number か、その子孫でなければならない、という意味になります。
Java
public static <T extends Number> double half(T value) {
return value.doubleValue() / 2;
}さっき弾かれた doubleValue() が、今度は通ります。T が必ず Number の仲間だと分かったので、Number が持つメソッドは安心して呼べるからです。
Java
half(10); // Integer は Number の子孫 ─ OK
half(2.5); // Double も OK
half("10"); // String は Number ではない ─ コンパイルエラー実行してから気づくのではなく、書いた時点で止まってくれるのが境界の値打ちです。
比べたいなら Comparable を境界にする
もう 1 つよく使うのが <T extends Comparable<T>> です。Comparable を実装した型に絞れば、compareTo で大小を比べられます。
Java
public static <T extends Comparable<T>> T bigger(T a, T b) {
return a.compareTo(b) >= 0 ? a : b;
}Integer String LocalDate はどれも Comparable を実装しているので、そのまま渡せます。ここで extends と書いているところに注目してください。境界にはクラスもインタフェースも指定できますが、インタフェースのときも implements ではなく extends を使う決まりです。ここは Java の文法の例外なので、そういうものだと覚えてしまうのが早道です。
条件を重ねたいときは & でつないで <T extends Number & Comparable<T>> のように書きます。クラスを含める場合は 1 つだけ、しかも先頭に置く必要があります。
課題そのものは int の配列を受け取って合計を返すだけなので、境界を書く場面はありません。まずは、境界がどんなときに効いてくるのかを上の 2 つの例で押さえておいてください。
要件
Solution.sumNumbers(int[] arr)をpublic static intで実装すること- 配列の全要素を足し合わせた結果を
intで返すこと - 空配列が渡されたら
0を返すこと (例外を投げない)
入出力例
sumNumbers([1,2,3]) → 6
sumNumbers([]) → 0
sumNumbers([-5,10,-3,8]) → 10
sumNumbers([42]) → 42