Javaを使っていると、Clazz<Foo extends Bar>
とかClazz<? extends T>
というような
あまり見た目にやさしくない呪文を見かけると思います。
これはなんだ?って話です。
これは上限付きワイルドカード(upper bounded wildcard)といいます。
そもそもなんでこれが必要なのか
メソッドやクラスを使う場合を考えてみると
どんな型でも受け入れたい場合どうすればよいか。
その場合はObjectを使います。。。もしくは?(unbounded wildcard)を使います。。。
ってのは冗談で、そうすると使いたいメソッドがObjectにないことが多くて
警告でcastしてないよ!!ってのがしょっちゅう出てきます。
そこで出てくるのがジェネリクス(Generics)です。
List<E>
とかがその典型ですね。
List<Integer>
とすればInteger型でListが扱えます。
List<String>
とすればString型でListが扱えます。
List<T>
だけで十分ではないの?って思うと思います。
たいていはそれで充分なのですが、なんかたまに
? extends T
ってのを見かける。これJava特有だし。(C#にはwhere
なるものがあるが…)
これライブラリでよく見ると思いますが、自分での実装で使う機会はそう多くないため、
いつ使うんだ?ってなりがちです。
具体例
例えばですが、配列を使っていてソートをするのに
挿入ソートのクラスでどんな型でも受け入れたいってことにしたい場合、
こいつが必要になってきます。
すごい単純なクラスを作ってみます。
単にInsertionSorter<T>
となっていないところがポイントです
import java.util.Arrays; public class InsertionSorter<T extends Comparable<T>> { T[] data; public void sort(T[] data) { this.data = data; isort(data); } private void isort(T[] data) { for (int left = 1; left < data.length; left++) { for (int right = left; right > 0 && data[right - 1].compareTo(data[right]) > 0; right--) { swap(data, right - 1, right); } } } public void swap(T[] arr, int pos1, int pos2) { T temp = arr[pos1]; arr[pos1] = arr[pos2]; arr[pos2] = temp; } public static void main(String[] args) { Integer[] data = { 5, 4, 3, 2, 6, 7, 8, 1, 9 }; InsertionSorter<Integer> sorter = new InsertionSorter<>(); sorter.sort(data); Arrays.stream(data).forEach(i -> System.out.print(i + ",")); } }
オンラインで実行したい方はこちら: http://tpcg.io/bTn8mbKH
sortをinterfaceのメソッドにしてなくてStrategyパターンになれないから微妙ですが…例なのでシンプルに。
<T extends Comparable<T>>
は
Comparable
要はComparableを継承するものに制限しています。
上記のクラスではcompareTo(T o)
を使っています。
compareTo(T o)
を使えるのはComparableな型でなくてはなりません。
ところが単にTとしてしまうとcompareTo(T o)
なんてメソッドありませんとエラーになってしまいます。
ピンとこない方は、上記のURLでInsertionSorter
のGenericsの部分を単にT
に変えてみてください。
実行しようとするとエラーになると思います。
それを解決するためにこのT extends Comparable<T>
というものがあります。
こうすると、「ほうほう、このTはComparableなんだな」とコンパイラがわかってくれてエラーにしなくなります。
参考リンク
https://www.ibm.com/developerworks/jp/java/library/j-jtp07018.html