1. ホーム
  2. java

[解決済み] ジェネリックメソッドとワイルドカードの使い分けは?

2022-06-26 21:13:17

質問

ジェネリックメソッドについて、以下の記事を読んでいます。 OracleDocGenericMethod . 私は、ワイルドカードを使用する場合とジェネリックメソッドを使用する場合の比較について、かなり混乱しています。 ドキュメントから引用します。

interface Collection<E> {
    public boolean containsAll(Collection<?> c);
    public boolean addAll(Collection<? extends E> c);
}

ここでは、代わりにジェネリックメソッドを使うことができました。

interface Collection<E> {
    public <T> boolean containsAll(Collection<T> c);
    public <T extends E> boolean addAll(Collection<T> c);
    // Hey, type variables can have bounds too!
}

[...] これはtype引数がポリモーフィズムのために使われていることを物語っています。 その唯一の効果は、実際の引数の様々な型を異なる呼び出し場所で使用できるようにすることです。 を異なる呼び出し位置で使用できるようにすることです。もしそうであれば はワイルドカードを使うべきである。ワイルドカードは柔軟なサブタイピングをサポートするために設計されています。 それがここで表現したいことなのです。

のようなワイルドカードを考えてはいけないのでしょうか? (Collection<? extends E> c); も一種の ポリモーフィズムをサポートしていると思いませんか?では、なぜジェネリックメソッドの使用は良くないとされているのでしょうか?

続けて、こう書かれています。

ジェネリックメソッドでは、型パラメータを使用して、メソッドへの1つ以上の引数の型の間の依存関係を表現することができます。 メソッドへの1つ以上の引数や戻り値の型の間の依存性を表現するために、型パラメータを使用することができます。 およびその戻り値の型の間の依存性を表現するために型パラメータを使用することができます。そのような依存関係がない場合、ジェネリック メソッドは使用されるべきではありません。

これは何を意味するのでしょうか?

例題を提示されました

class Collections {
    public static <T> void copy(List<T> dest, List<? extends T> src) {
    ...
}

[...]

このメソッドの署名は別の方法で書くことができます。 ワイルドカードを全く使わずに。

class Collections {
    public static <T, S extends T> void copy(List<T> dest, List<S> src) {
    ...
}

この文書では、2番目の宣言は推奨されず、1番目の構文の使用が推奨されているのですか?最初の宣言と2番目の宣言の違いは何ですか?どちらも同じことをやっているように見えますが?

誰かがこの領域に光を置くことができます。

どのように解決するのですか?

ワイルドカードと型パラメータで同じことができる場所があります。しかし、タイプパラメータを使用しなければならない場所もあります。

  1. 異なるタイプのメソッド引数に何らかの関係を強制したい場合、ワイルドカードではそれを行うことができず、タイプパラメーターを使用する必要があります。

あなたのメソッドを例にとって、もしあなたが srcdest に渡されるリスト copy() メソッドに渡されるリストは、同じパラメータ化された型である必要があります。

public static <T extends Number> void copy(List<T> dest, List<T> src)

ここでは destsrc は、同じパラメタライズドタイプを持つ List . ですから src から dest .

しかし、そのままワイルドカードを使用する方法に変更すると

public static void copy(List<? extends Number> dest, List<? extends Number> src)

を指定しても、期待通りに動作しません。2番目のケースは List<Integer>List<Float> として dest そして src . ですから、要素を src から dest はもう型安全でなくなってしまう。 もし、そのような関係が必要ないのであれば、型パラメータを全く使わないのも自由です。

その他、ワイルドカードと型パラメーターの違いは以下の通りです。

  • パラメータ化された型引数が1つしかない場合、型パラメータも機能しますが、ワイルドカードを使用することができます。
  • 型パラメータは複数の境界をサポートしますが、ワイルドカードはサポートしません。
  • ワイルドカードは上限と下限の両方をサポートし、型パラメータは上限のみをサポートします。そのため、もしあなたが List という型の Integer またはそのスーパークラスであれば、可能です。

    public void print(List<? super Integer> list)  // OK
    
    

    のようになりますが、typeパラメータは使えません。

     public <T super Integer> void print(List<T> list)  // Won't compile
    
    

リファレンスです。