最近、List<? super RationalNumber>
として宣言されたリストにNumber
オブジェクトを挿入できない理由を説明したときに、私が間違っていると言う人がいました。 (これはRationalNumber
がNumber
のサブクラスであることを前提としています)
私の説明は、List<? super RationalNumber>
では<? super RationalNumber>
はRationalNumber
の不明なスーパークラスを表すということです。つまり、<? super RationalNumber>
は、List<? super RationalNumber>
の有効なオブジェクトが、RationalNumber
の不明なスーパークラスを拡張するオブジェクトであることを示します。
RationalNumber
の不明なスーパークラスは本当に不明であるため(たとえば、X
ではなくインターフェイスNumber
になる場合があります)、Number
がX
のサブクラス、つまりX
を実装しているため、Number
は<? super RationalNumber>
の有効な置換ではありません。
この説明の何が問題になっていますか?編集:私が説明する方法、私の説明に何が悪いのかを尋ねていることに注意してください。元の質問に対する別の説明ではありません。
ありがとう。
List<? super RationalNumber>
は、RationalNumberのスーパークラスであるX型のリストです。 RationalNumber
はNumber
のサブクラスであるため、Xの候補はRationalNumber
、Number
、または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);
以下も参照してください。
余談です。スキップして構いません。
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(containerOne
、containerTwo
、上記のサンプルのcontainerThree
)から:
T
がRationalNumber
に安全にアップキャストできることがわかっている場合は常に、(型パラメーターT
の)ジェネリッククラスのインスタンスが割り当てられます。今、私たちはこの知識を使って質問に答えようとします:Number
をそのような "handle"に渡すことができますか?LBWCを持つ汎用クラスof RationalNumber
?
とすれば
new List<RationalNumber>()
のインスタンスをList<? super RationalNumber> varGeneric
のハンドルに割り当てることは有効です。私たちはこの当然の質問を得ます:
new List<RationalNumber>().add(new Number(...))
を呼び出せますか?これが起こらないようにするのがLBWCの設計目標であることを考えると、この結論を導き出すことができます。
Number
であるタイプ?
のRationalNumber
を渡す)が発生しないようにする必要があります。これは、設計の観点からの質問に答えます。強制の問題に答えられるのは、Java言語とそのコンパイラーツールの構築に精通しているコミュニティの上級メンバーだけです。一般の人々からのコメントは、推測、または「仮説」または「理論」。