Gobble up pudding

プログラミングの記事がメインのブログです。

MENU

上限付き境界ワイルドカードについて

スポンサードリンク

f:id:fa11enprince:20200511185745j:plain 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を継承するTって意味です(extends継承だけでなくimplements継承も含まれます)。
要は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