Javaプログラミング言語では、ワイルドカード?は特別な種類の型引数[ 1 ]であり、ジェネリック(パラメータ化)型の使用における型の安全性を制御します。 [ 2 ]変数の宣言とインスタンス化、メソッドの定義では使用できますが、ジェネリック型の定義では使用できません。[ 3 ] [ 4 ]これは、 C#やScalaの定義サイト変異注釈とは対照的に、使用サイト変異注釈の一種です。
配列( Javaでは共変である[ 2 ])とは異なり、ジェネリック型の異なるインスタンス化は、明示的にも互いに互換性がありません。[ 2 ]例えば、宣言
ジェネリック<スーパータイプ> superGeneric ;ジェネリック<サブタイプ> subGeneric ;コンパイラは(Generic<Subtype>)superGenericキャストとの両方に対して変換エラーを報告します(Generic<Supertype>)subGeneric。
?この非互換性は、ワイルドカード を実際の型パラメータとして使用すると緩和されます。 [ 2 ]Generic<?>はジェネリック型 のすべてのパラメータ化のスーパータイプですGeneric。これによりGeneric<Supertype>、型 と のオブジェクトをGeneric<Subtype>型 の変数またはメソッドパラメータに安全に代入できますGeneric<?>。[ 2 ]を使用すると同じことが可能になりますが、互換性はとその子に制限されます。[ 5 ]もう1つの可能性は で、これも両方のオブジェクトを受け入れますが、互換性はとそのすべての親に制限されます。[ 5 ]Generic<? extends Supertype>SupertypeGeneric<? super Subtype>Subtype
ジェネリックユニットの本体では、(仮の)型パラメータはその上限(制約されていない場合はextends;で表される)のように扱われます。 [ 5 ]メソッドの戻り値の型が型パラメータである場合、その結果(たとえば、型)は上限の型(または)の変数によって参照できます。逆の場合、ワイルドカードは他の型には適合せず、 にも適合しません。 がメソッドの仮の型パラメータとして適用されている場合、実パラメータをそれに渡すことはできません。ただし、不明な型のオブジェクトをジェネリックオブジェクトから読み取り、上限のスーパータイプの変数に割り当てることはできます。 Object?ObjectObject?
クラスのサンプルコード: Generic<T extends UpperBound>
クラスGeneric < T extends UpperBound > { private T t ;パブリックジェネリック( Tt ) { this.t = t ; }パブリックvoid set ( T t ) { this . t = t ; }パブリックT get () { tを返す; } }クラスを使用するサンプルコード: Generic<T extends UpperBound>
... final Generic < UpperBound > concreteTypeReference = new Generic <> (); final Generic <?> wildcardReference = concreteTypeReference ; final UpperBound ub = wildcardReference . get (); // Object も OK wildcardReference . set ( new Object ()); // 型エラーwildcardReference . set ( new UpperBound ()); // 型エラーconcreteTypeReference . set ( new UpperBound ()); // OK ...境界付きワイルドカードとは、上限または下限の継承制約を持つワイルドカードです。ワイルドカードの境界は、クラス型、インターフェース型、配列型、または型変数のextendsいずれかです。上限はキーワード、下限はキーワードで表しますsuper。ワイルドカードには、上限または下限のいずれか一方しか境界を持つことはできません。
ワイルドカードの上限は、対応するジェネリック型で宣言された対応する型パラメータの上限のサブタイプでなければなりません。[ 5 ]上限を明示的に示すワイルドカードの例は次のとおりです。
Generic<? extends SubtypeOfUpperBound> referenceConstrainedFromAbove;
Genericこの参照は、 の型引数が のサブタイプであるの任意のパラメータ化を保持できますSubtypeOfUpperBound。Java のすべての参照型は Object のサブタイプであるため、上限を明示的に指定しないワイルドカードは、制約 を持つワイルドカードと実質的に同じですextends Object。
下限値を持つワイルドカード、例:
Generic<? super SubtypeOfUpperBound> referenceConstrainedFromBelow;
Genericは、その任意の型引数が対応する型パラメータの上限のサブタイプであり、かつ のスーパータイプであるような の任意のパラメータ化を保持できるSubtypeOfUpperBound。[ 5 ]
ワイルドカード型引数を使用してオブジェクトを作成することはできません。例えば、new Generic<?>()は禁止されています。実際には、 型の変数に代入可能なオブジェクトを作成したい場合はGeneric<?>、任意の型(ワイルドカードの制約内であれば)を型引数として使用できるため、これは不要です。
ただし、new ArrayList<Generic<?>>()ワイルドカードはインスタンス化された型 のパラメータではないため、 は許可されますArrayList。 についても同様ですnew ArrayList<List<?>>()。
配列作成式において、配列のコンポーネント型は、Java言語仕様のセクション4.7で定義されているように、具象化可能でなければなりません。これは、配列のコンポーネント型に型引数がある場合、それらはすべて無制限のワイルドカード( のみで構成されるワイルドカード?)でなければならないことを意味します。例えば、new Generic<?>[20]は正しいですが、 はnew Generic<SomeType>[20]正しくありません。
どちらの場合も、パラメータを使わないという選択肢もあります。ただし、型安全性が低いため、警告が表示されます(Raw型を参照)。
Javaコレクションフレームワークでは、クラスはList<MyClass>型のオブジェクトの順序付きコレクションを表しますMyClass。上限は次のように指定されますextends。Aは、のサブクラスのオブジェクトのリストです。つまり、リスト内の任意のオブジェクトは型であることが保証されているため、型の変数を使用して反復処理を行うことができます[ 6 ]。List<? extends MyClass>MyClassMyClassMyClass
public void doSomething ( List <? extends MyClass > list ) { for ( final MyClass object : list ) { // OK // 何かする} }MyClassただし、そのリストに 任意のタイプのオブジェクトを追加できるという保証はありません。
public void doSomething ( List <? extends MyClass > list ) { final MyClass m = new MyClass (); list . add ( m ); // コンパイルエラー}下限については逆のことが当てはまり、次のように指定しますsuper。Aは のスーパークラスのオブジェクトのリストです。つまり、リストには 型の任意のオブジェクトを含めることができることが保証されているため、 型の任意のオブジェクトを追加できます。 List<? super MyClass>MyClassMyClassMyClass
public void doSomething ( List <? super MyClass > list ) { final MyClass m = new MyClass (); list . add ( m ); // OK }ただし、 型の変数を使用してそのリストを反復処理できるかどうかは保証されませんMyClass。
public void doSomething ( List <? super MyClass > list ) { for ( final MyClass object : list ) { // コンパイルエラー// 何かする} }MyClass型のオブジェクトをリストに追加し、型の変数を使用して反復処理するにはMyClass、と の両方であるList<MyClass>唯一の型である が必要です。[ 7 ]ListList<? extends MyClass>List<? super MyClass>
ジョシュア・ブロック著『 Effective Java』に出てくる記憶術PECS(Producer Extends, Consumer Super)は、Javaでワイルドカード(共変性と反変性に対応)をいつ使用するかを覚えるのに簡単な方法を教えてくれます。[ 5 ]
C++では、ジェネリック型の制約はconceptsを使用して表現できます。
stdをインポートします。std :: is_base_of_vを使用します。std :: vectorを使用します。クラスPlayer { // ... };テンプレート< typename T > concept ExtendsPlayer = is_base_of_v < Player , T > ;// T は継承の上限が Player である型である必要があり、Player から継承しない型はブロックされます。template < ExtendsPlayer T > void processListOfPlayers ( vector < T > players ) { // ... }この例は、次の Java コードと同等です。
java.util.Listをインポートします。クラスPlayer { // ... }public class Example { // T は Player から継承する型に制限されますpublic static < T extends Player > void processListOfPlayers ( List < T > players ) { // ... } }C#では、ジェネリック型制約はwhere句で表現されます。これはJavaのワイルドカードよりも表現力が高く、強力です。[ 8 ]
システムを使用します。public class MyGenericClass < T , U > ( T : IComparable < T > ) 、ref struct を許可( U : class 、notnull 、new () { // ... }Kotlin はJVM言語ですが、Javaスタイルの型ワイルドカードをサポートしていません。ただし、 は(例えば)?として表されます。それ以外はC#スタイルの句があります。[ 9 ]*List<*>where
fun < T > copyWhenGreater ( list : List < T > , threshold : T ): List < String > where T : CharSequence , T : Comparable < T > { return list . filter { it > threshold }. map { it . toString () } }Rust では、特性をバインドするために句も使用されますwhere。
std :: cmp :: Ordを使用します。構造体MyStruct < T >ここでT : Ord + Default { value : T }impl < T > MyStruct < T > where T : Ord + Default { fn new ( value : T ) -> Self { MyStruct { value } }fn is_less_than ( & self , other : & Self ) -> bool { self . value < other . value } }