ジェネリック メソッドの複数のワイルドカード: Java コンパイラ パズル
はじめに
Java ジェネリックス、ワイルドカード (*) は不明なタイプを表します。ジェネリック メソッドで複数のワイルドカードを使用すると、混乱や予期しない動作が発生する可能性があります。この記事では、複数のワイルドカードの複雑さと、それらが Java のタイプ セーフに与える影響について検証します。
混乱
次のコードを考えてみましょう:
public class TwoListsOfUnknowns { static void doNothing(List<?> list1, List<?> list2) { } public static void main(String[] args) { List<String> list1 = null; List<Integer> list2 = null; doNothing(list1, list2); // compiles fine! } }
doNothing の 2 つのワイルドカードは無関係であるように見えるため、リストおよび List
static void doSomethingIllegal(List<?> list1, List<?> list2) { list1.addAll(list2); // DOES NOT COMPILE!!! }
これは、list1 と list2 が異なる型である可能性があるものの、直接使用できない何らかの接続がある可能性があることを示唆しています。
入れ子になったワイルドカードの混乱
さらなる調査により、混乱は複数の場所にあるわけではないことが明らかになりました。ワイルドカード、ただしネストされたワイルドカード内:
public class LOLUnknowns1 { static void probablyIllegal(List<List<?>> lol, List<?> list) { lol.add(list); // this compiles!! how come??? } }
リストが lol の要素とは異なる型であっても、このコードはエラーなしでコンパイルされます。ただし、このシナリオでは型安全性に関する疑問が生じることに注意することが重要です。
真実: キャプチャ変換
この混乱は、キャプチャ変換と呼ばれる概念から生じています。これにより、ジェネリック メソッドで使用される場合、特定のワイルドカードが特定の型をキャプチャできるようになります。これが、おそらく Illegal の次のバリエーションがコンパイルされる理由です。
static void probablyIllegalAgain(List<List<? extends Number>> lol, List<? extends Number> list) { lol.add(list); // compiles fine!!! how come??? }
ここで、lol のワイルドカードは、List
入れ子になったワイルドカードについて
重要なポイントは、複数のワイルドカード自体には問題がないということです。混乱は、型の差異により「互換性」がない型をキャプチャするためにネストされたワイルドカードを使用しようとすると発生します。
LOLUnknowns1 の場合、List>> 内のネストされたワイルドカードは、 lol のすべての要素タイプに対してキャプチャが安全ではないため、特定のタイプをキャプチャできません。これが、リストがどのような型であってもよく、潜在的な型安全性の問題につながる理由です。
結論
ジェネリック メソッドの複数のワイルドカードは混乱を招く可能性がありますが、キャプチャ変換とその制限は非常に重要です。入れ子になったワイルドカードでは、型の安全性を確保するために慎重な考慮が必要です。これらの原則に従うことで、Java ジェネリックの複雑さを理解し、堅牢なコードを作成できます。
以上がJava のジェネリック メソッドに複数のワイルドカードを使用すると混乱が生じるのはなぜですか?の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。