0Pricing
Java Academy · レッスン

下限付きワイルドカード

消費側には ? super T を使います。

「下限付きワイルドカード」はCoddyKit上の無料Java Academyレッスンです。 これはレッスン2/4です。 下記で完全なレッスンを無料で読むことができます。その後、ブラウザ内の組み込みコードエディタと24時間対応のAIチューターでハンズオン演習できます。 これはJava Academy学習パスの一部であり、ウェブとCoddyKitアプリ全体で進捗が同期されます。 Java Academyコースには全4レッスンが含まれています。

下限付きワイルドカード

? super T は下限付きワイルドカードです。これは「T または T のスーパータイプである、未知の型」を意味します。

List<? super Integer> は、List<Integer>、List<Number>、または List<Object> を参照できます。

import java.util.ArrayList;
import java.util.List;

public class Main {
    public static void main(String[] args) {
        List<? super Integer> a = new ArrayList<Number>();
        List<? super Integer> b = new ArrayList<Object>();
        a.add(1);
        b.add(2);
        System.out.println(a + " " + b);
    }
}

コンシューマーへの書き込み

? super Integer では、任意の Integer(または Integer のサブタイプ)を安全に追加できます。

実際のリスト型が何であっても、少なくとも Integer のスーパータイプのリストなので、Integer は必ず適合します。

import java.util.ArrayList;
import java.util.List;

public class Main {
    static void addNumbers(List<? super Integer> dest) {
        for (int i = 1; i <= 3; i++) dest.add(i);
    }
    public static void main(String[] args) {
        List<Number> nums = new ArrayList<>();
        addNumbers(nums);
        System.out.println(nums);
    }
}

読み取り結果は Object になります

その代わり、? super T のリストから読み取る場合、コンパイラが把握できるのは、要素が T の未知のスーパータイプのいずれかであるということだけです。

読み取り時に安全な唯一の型は Object です。

import java.util.ArrayList;
import java.util.List;

public class Main {
    public static void main(String[] args) {
        List<? super Integer> list = new ArrayList<Number>();
        list.add(10);
        Object o = list.get(0); // only Object is guaranteed
        // Integer i = list.get(0); // would NOT compile
        System.out.println(o);
    }
}

読み取りが制限される理由

List<? super Integer> は、実際には String を格納した List<Object> かもしれません。

そのため、読み取り結果は任意の Object になり得ます。コンパイラは Integer であると保証できないため、読み取り結果は Object にしかなりません。

import java.util.ArrayList;
import java.util.List;

public class Main {
    public static void main(String[] args) {
        List<Object> raw = new ArrayList<>();
        raw.add("a string");
        List<? super Integer> view = raw; // legal: Object is a supertype of Integer
        view.add(5);
        System.out.println(view.get(0).getClass().getSimpleName());
    }
}

コンシューマーのユースケース

メソッドが値を消費(書き込み)するだけの場合は、? super T を使用してください。

宛先コレクションに値を格納するメソッドが、典型的なケースです。

import java.util.ArrayList;
import java.util.List;

public class Main {
    static void fill(List<? super String> dest, int count) {
        for (int i = 0; i < count; i++) dest.add("item" + i);
    }
    public static void main(String[] args) {
        List<Object> objs = new ArrayList<>();
        fill(objs, 3);
        System.out.println(objs);
    }
}

サブタイプの追加も可能です

? super Number では、Number またはそのサブタイプである Integer、Double、Long を追加できます。

下限によって、リストが少なくとも Number を受け入れることが保証されるため、すべてのサブタイプが適合します。

import java.util.ArrayList;
import java.util.List;

public class Main {
    public static void main(String[] args) {
        List<? super Number> list = new ArrayList<Object>();
        list.add(1);      // Integer
        list.add(2.5);    // Double
        list.add(3L);     // Long
        System.out.println(list);
    }
}

Collections.addAll の形式

JDK では、データを受け取る側に下限付きワイルドカードを使用します。たとえば、宛先に要素を追加するジェネリックメソッドなどです。

import java.util.ArrayList;
import java.util.List;

public class Main {
    @SafeVarargs
    static <T> void addAll(List<? super T> dest, T... items) {
        for (T item : items) dest.add(item);
    }
    public static void main(String[] args) {
        List<Object> sink = new ArrayList<>();
        addAll(sink, "a", "b", "c");
        System.out.println(sink);
    }
}

super を使う Comparator

実際の使用例として、Comparator<? super T> を使うと、スーパータイプ用に作成されたコンパレータをソートで受け入れられます。

任意の Object を toString で比較するコンパレータで、String のリストをソートできます。

import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;

public class Main {
    public static void main(String[] args) {
        Comparator<Object> byString = Comparator.comparing(Object::toString);
        List<String> names = new ArrayList<>(List.of("Charlie", "Alice", "Bob"));
        names.sort(byString); // List<String>.sort accepts Comparator<? super String>
        System.out.println(names);
    }
}

super と extends は対になる概念です

2つのワイルドカードは正反対の関係にあります。

  • ? extends T:T として読み取れますが、書き込めません。プロデューサーです。
  • ? super T:T を書き込めますが、読み取り結果は Object だけです。コンシューマーです。

この対称性が、次に学ぶ PECS ルールの核心です。

import java.util.ArrayList;
import java.util.List;

public class Main {
    public static void main(String[] args) {
        List<? super Integer> consumer = new ArrayList<Number>();
        consumer.add(1);
        System.out.println("consumer can write: " + consumer);
    }
}

境界の異なる型の間でコピーする

コピー用メソッドを使うと、2つのワイルドカードを同時に確認できます。? extends T から読み取り、? super T に書き込みます。

import java.util.ArrayList;
import java.util.List;

public class Main {
    static <T> void copy(List<? extends T> src, List<? super T> dest) {
        for (T t : src) dest.add(t);
    }
    public static void main(String[] args) {
        List<Integer> src = List.of(1, 2, 3);
        List<Number> dest = new ArrayList<>();
        copy(src, dest);
        System.out.println(dest);
    }
}

ワイルドカードを使わない場合

メソッドが T の読み取りと書き込みの両方を行う場合は、ワイルドカードを使用しないでください。正確な型または名前付き型パラメータを使用します。

ワイルドカードは、読み取り専用または書き込み専用のケースのために存在します。

import java.util.ArrayList;
import java.util.List;

public class Main {
    static <T> void swapFirstTwo(List<T> list) {
        T tmp = list.get(0);
        list.set(0, list.get(1));
        list.set(1, tmp);
    }
    public static void main(String[] args) {
        List<String> list = new ArrayList<>(List.of("a", "b"));
        swapFirstTwo(list);
        System.out.println(list);
    }
}

理解度チェック

下限付きワイルドカードについての理解を確認しましょう。

まとめ

下限付きワイルドカードについて学びました。

  • ? super T は、T の未知のスーパータイプを意味します。
  • T とそのサブタイプを書き込めます(コンシューマー)。
  • 読み取り結果はObject だけです。
  • ? extends T と正反対の関係にあります。

次は、これら2つを結び付けるルール、PECS です。

import java.util.ArrayList;
import java.util.List;

public class Main {
    public static void main(String[] args) {
        List<? super Integer> list = new ArrayList<Number>();
        list.add(42);
        System.out.println("Lower bound recap: " + list);
    }
}

よくある質問

「下限付きワイルドカード」レッスンは無料ですか?

はい。「下限付きワイルドカード」の完全なテキストはこのウェブで無料で読めます。インタラクティブに演習し(組み込みコードエディタと24時間対応のAIチューター)、Java Academyコースの残りをアンロックするには、CoddyKit PROにアップグレードしてください。 Java Academyコースには全4レッスンが含まれています。

「下限付きワイルドカード」で何を学びますか?

消費側には ? super T を使います。 ブラウザで直接実行するハンズオンコードでJava Academyを演習し、24時間対応のAIチューターがレッスンを進める中での質問に答えます。

Java Academyを始めるのに経験は必要ですか?

事前経験は必要ありません。CoddyKitのJava Academyは初級者から上級者向けに構成されているため、ここから始めるか最初から始めて、自分のペースで進むことができます。 これはレッスン2/4です。

「下限付きワイルドカード」レッスンにはどのくらい時間がかかりますか?

ほとんどのCoddyKitレッスンは約5~10分かかります。各レッスンはコンパクトでインタラクティブなので、着実に進歩し、ウェブとアプリ全体で正確に前回の場所から再開できます。

このJava Academyレッスンでコードを書いて実行できますか?

はい。すべてのJava Academyレッスンに組み込みコードエディタが含まれているため、ブラウザでリアルコードを書いて実行し、即座のAIフィードバックを取得できます。ローカル設定は不要です。

このコースのすべてのレッスン

  1. 上限付きワイルドカード
  2. 下限付きワイルドカード
  3. PECS 原則
  4. API でのワイルドカード
← Java Academyに戻る