上限付きワイルドカード
生成側には ? 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フィードバックを取得できます。ローカル設定は不要です。
このコースのすべてのレッスン
- 上限付きワイルドカード
- 下限付きワイルドカード
- PECS 原則
- API でのワイルドカード