web-dev-qa-db-ja.com

理解Java汎用キーワード 'super'

最近、List<? super RationalNumber>として宣言されたリストにNumberオブジェクトを挿入できない理由を説明したときに、私が間違っていると言う人がいました。 (これはRationalNumberNumberのサブクラスであることを前提としています)

私の説明は、List<? super RationalNumber>では<? super RationalNumber>RationalNumberの不明なスーパークラスを表すということです。つまり、<? super RationalNumber>は、List<? super RationalNumber>の有効なオブジェクトが、RationalNumberの不明なスーパークラスを拡張するオブジェクトであることを示します。

RationalNumberの不明なスーパークラスは本当に不明であるため(たとえば、XではなくインターフェイスNumberになる場合があります)、NumberXのサブクラス、つまりXを実装しているため、Number<? super RationalNumber>の有効な置換ではありません。

この説明の何が問題になっていますか?編集:私が説明する方法、私の説明に何が悪いのかを尋ねていることに注意してください。元の質問に対する別の説明ではありません。

ありがとう。

7
InformedA

List<? super RationalNumber>は、RationalNumberのスーパークラスであるX型のリストです。 RationalNumberNumberのサブクラスであるため、Xの候補はRationalNumberNumber、またはObjectです。最も制限的な可能性は、XがRationalNumber自体であることです。 List<RationalNumber>にはRationalNumberのインスタンスのみを含めることができ、Numberのインスタンスを含めることはできません。

つまり、List<? super T>はTのインスタンスを受け入れることができるリストですが、現在の内容は不明なタイプです。これは、Tのインスタンスを含むList<? extends T>とは対照的ですが、受け入れられる型が不明であるため、新しい要素を追加できません。

この例は、superの使用法を示しています。

void storePies(List<? super RationalNumber> list) { 
    list.add(new RationalNumber(22, 7))
    list.add(new RationalNumber(355, 113))
}

...
List<Number> list = new ArrayList<Number>();
storePies(list);
list.add(3.14159265358979);

この例は、extendsの使用法を示しています。

double sum(Collection<? extends Number> numbers) {
    double s = 0;
    for (Number n: numbers) { s += n.doubleValue(); }
    return s;
}

List<Integer> somePrimes = new ArrayList<Integer>();
somePrimes.add(5);
somePrimes.add(17);
double total = sum(somePrimes);
7
kevin cline

以下も参照してください。


余談です。スキップして構いません。

C#にも精通しているプログラマーのために、同様の機能が存在します。

共分散と反分散という用語は、 Liskov置換原理(LSP) からの初期の議論にさかのぼることができます。


下限ワイルドカード(LBWC)の設計目標は、次のコードの型安全性を保証することです。

(これは1つの例にすぎません。非常に単純な例の1つです。)

List<? super RationalNumber> containerOne = new ArrayList<RationalNumber>(); 
List<? super RationalNumber> containerTwo = new ArrayList<Number>(); 
List<? super RationalNumber> containerThree = new ArrayList<Object>(); 

RationalNumber itemValue = new RationalNumber(22, 7); 

containerOne.add(itemValue); 
containerTwo.add(itemValue); 
containerThree.add(itemValue);

要するに、LBWCは "handles"-LBWC(containerOnecontainerTwo、上記のサンプルのcontainerThree)から:

  • TRationalNumberに安全にアップキャストできることがわかっている場合は常に、(型パラメーターTの)ジェネリッククラスのインスタンスが割り当てられます。

今、私たちはこの知識を使って質問に答えようとします:Numberをそのような "handle"に渡すことができますか?LBWCを持つ汎用クラスof RationalNumber


とすれば

  • new List<RationalNumber>()のインスタンスをList<? super RationalNumber> varGenericのハンドルに割り当てることは有効です。
  • もちろん、まったく別のことをすることは有効です。しかし、LBWCの設計では、これはが対処する必要がある状況です。

私たちはこの当然の質問を得ます:

  • new List<RationalNumber>().add(new Number(...))を呼び出せますか?
    • もちろん違います。

これが起こらないようにするのがLBWCの設計目標であることを考えると、この結論を導き出すことができます。

  • LBWCが(コンパイラーまたは言語警察によって)強制可能であるように設計されている場合、この割り当て(LBWCがNumberであるタイプ?RationalNumberを渡す)が発生しないようにする必要があります。

これは、設計の観点からの質問に答えます。強制の問題に答えられるのは、Java言語とそのコンパイラーツールの構築に精通しているコミュニティの上級メンバーだけです。一般の人々からのコメントは、推測、または「仮説」または「理論」。

5
rwong