0Pricing
Java Academy · レッスン

上限付きワイルドカード

生成側には ? extends T を使います。

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

ジェネリクスは不変です

Java のジェネリクスは 不変 です。List<Integer> は ではありません。List<Number> です。Integer は Number ですが、List<Integer> は List<Number> ではありません。

これは初心者には意外ですが、型システムの健全性を保つための仕組みです。ワイルドカードを使うと、この制限を安全に緩和できます。

import java.util.List;

public class Main {
    public static void main(String[] args) {
        List<Integer> ints = List.of(1, 2, 3);
        // List<Number> nums = ints; // would NOT compile
        System.out.println("List<Integer> is not List<Number>");
        System.out.println(ints);
    }
}

上限付きワイルドカード

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

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

import java.util.List;

public class Main {
    public static void main(String[] args) {
        List<? extends Number> a = List.of(1, 2, 3);
        List<? extends Number> b = List.of(1.5, 2.5);
        System.out.println(a.get(0));
        System.out.println(b.get(0));
    }
}

生成元から読み取る

? extends Number を使うと、要素を Number として安全に読み取れます。

実際の要素型が何であっても、少なくとも Number であることが保証されるため、読み取りは型安全です。

import java.util.List;

public class Main {
    static double sum(List<? extends Number> list) {
        double total = 0;
        for (Number n : list) total += n.doubleValue();
        return total;
    }
    public static void main(String[] args) {
        System.out.println(sum(List.of(1, 2, 3)));
        System.out.println(sum(List.of(1.5, 2.5)));
    }
}

書き込むことはできません

ここが注意点です。? extends Number のリストには、null 以外の要素を追加できません。

コンパイラには、正確な要素型がわかりません。List<Integer> である可能性があるため、Double を追加すると安全ではないからです。

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

public class Main {
    public static void main(String[] args) {
        List<? extends Number> list = new ArrayList<Integer>();
        // list.add(1);   // does NOT compile
        // list.add(1.5); // does NOT compile
        System.out.println("Cannot add to ? extends Number");
        System.out.println("size = " + list.size());
    }
}

書き込みが禁止される理由

書き込みが許可されていた場合を考えてみてください。List<Integer> を List<? extends Number> として渡し、そこに Double を追加できてしまいます。

後でリストを List<Integer> として読み取るコードは、ClassCastException を受け取ることになります。コンパイラは、書き込みを禁止することでこれを防ぎます。

import java.util.List;

public class Main {
    public static void main(String[] args) {
        // Conceptual: this is why add is blocked
        System.out.println("Writing a Double into a List<Integer> would corrupt it");
        System.out.println("so ? extends bans all adds");
    }
}

プロデューサーのユースケース

メソッドが値を生成(読み出し)するだけの場合は、? extends T を使用してください。

ソースコレクションから要素をコピーして取り出すメソッドが、まさにその例です。

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

public class Main {
    static List<Number> copyOut(List<? extends Number> source) {
        List<Number> dest = new ArrayList<>();
        for (Number n : source) dest.add(n);
        return dest;
    }
    public static void main(String[] args) {
        System.out.println(copyOut(List.of(1, 2, 3)));
    }
}

上限付き型パラメータとワイルドカード

<T extends Number>(上限付き型パラメータ)と、? extends Number(ワイルドカード)を混同しないでください。

  • 型パラメータ:メソッド全体で使用する型に名前を付けます。
  • ワイルドカード:名前を付ける必要がない場合に使用する、名前のない未知のサブタイプです。
import java.util.List;

public class Main {
    static <T extends Number> T first(List<T> list) { return list.get(0); }
    public static void main(String[] args) {
        Integer i = first(List.of(10, 20));
        System.out.println(i);
    }
}

複数の境界

型パラメータには、& を使って複数の境界を指定できます。たとえば <T extends Number & Comparable<T>> のように記述します。

ワイルドカードがサポートする上限は1つだけなので、複数の境界が必要な場合は名前付き型パラメータを使用します。

import java.util.List;

public class Main {
    static <T extends Number & Comparable<T>> T max(List<T> list) {
        T best = list.get(0);
        for (T t : list) if (t.compareTo(best) > 0) best = t;
        return best;
    }
    public static void main(String[] args) {
        System.out.println(max(List.of(3, 9, 1, 7)));
    }
}

Collections.max でのワイルドカード

JDK では上限付きワイルドカードが広く使用されています。たとえば、読み取り専用コレクションの合計を求めたり、走査したりする場合です。

ここでは、プロデューサーとして型付けされたリストから最大値を求めます。

import java.util.List;

public class Main {
    static double largest(List<? extends Number> nums) {
        double max = Double.NEGATIVE_INFINITY;
        for (Number n : nums) max = Math.max(max, n.doubleValue());
        return max;
    }
    public static void main(String[] args) {
        System.out.println(largest(List.of(4, 2, 9, 1)));
        System.out.println(largest(List.of(0.5, 9.9, 3.3)));
    }
}

書き込み可能な値は null だけです

? extends T のコレクションに追加できる唯一のものは null です。null は任意の参照型に代入できるためです。

これはほとんど役に立ちませんが、add が完全に禁止されているのではなく、実質的に禁止されているだけである理由を説明しています。

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

public class Main {
    public static void main(String[] args) {
        List<? extends Number> list = new ArrayList<Number>();
        // Only null is assignable; demonstrated conceptually
        System.out.println("Only null could be added; we avoid that");
        System.out.println("size = " + list.size());
    }
}

Object として読み出すことは常に可能です

すべての型は Object を継承するため、任意のワイルドカードリストの要素を常に Object として読み取れます。

上限が Number の場合は、さらに Number として直接読み取れるという利点もあります。

import java.util.List;

public class Main {
    public static void main(String[] args) {
        List<? extends Number> nums = List.of(1, 2.0, 3L);
        for (Object o : nums) System.out.println(o.getClass().getSimpleName());
    }
}

理解度チェック

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

まとめ

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

  • ? extends T は、T の未知のサブタイプを意味します。
  • 要素を T として読み取れます(プロデューサー)。
  • null 以外の要素は追加できません。
  • ジェネリクスは不変ですが、ワイルドカードを使うと安全に柔軟性を持たせられます。

次は、その鏡像である下限付きワイルドカードです。

import java.util.List;

public class Main {
    public static void main(String[] args) {
        List<? extends Number> nums = List.of(1, 2, 3);
        System.out.println("Upper bound recap: read " + nums.get(0));
    }
}

よくある質問

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

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

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

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

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

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

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

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

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

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

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

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