型とサブタイプの間の関係についてであると言う人もいれば、型変換についてであると言う人もいれば、メソッドを上書きするかオーバーロードするかを決定するために使用する人もいます。
上記のすべて。
基本的に、これらの用語は、サブタイプ関係がタイプ変換によってどのように影響を受けるかを説明します。つまり、A
およびB
がタイプでf
あり、タイプ変換であり、≤サブタイプの関係(つまり、のサブタイプであるA ≤ B
ことを意味する)である場合、A
B
f
それがA ≤ B
意味する場合、共変ですf(A) ≤ f(B)
f
をA ≤ B
意味する場合、反変ですf(B) ≤ f(A)
f
上記のいずれも成立しない場合は不変
例を考えてみましょう。f(A) = List<A>
どこList
で宣言されているかみましょう
class List<T> { ... }
あるf
共変、反変、または不変は?共変とは、a List<String>
がのサブタイプ、aがサブタイプでList<Object>
ある反変List<Object>
、List<String>
およびどちらも他のサブタイプではない不変、つまり変換不可能なタイプList<String>
でList<Object>
あることを意味します。Javaでは後者が真実であり、ジェネリックは不変であると(ある程度非公式に)言います。
もう一つの例。みましょうf(A) = A[]
。あるf
共変、反変、または不変は?つまり、String []はObject []のサブタイプ、Object []はString []のサブタイプですか、それとも他のサブタイプでもありませんか?(回答:Javaでは、配列は共変です)
これはまだかなり抽象的なものでした。より具体的にするために、Javaのどの操作がサブタイプの関係で定義されているかを見てみましょう。最も簡単な例は代入です。ステートメント
x = y;
コンパイルされるのはtypeof(y) ≤ typeof(x)
。つまり、ステートメントが
ArrayList<String> strings = new ArrayList<Object>();
ArrayList<Object> objects = new ArrayList<String>();
Javaでコンパイルされませんが、
Object[] objects = new String[1];
意志。
サブタイプの関係が重要なもう1つの例は、メソッド呼び出し式です。
result = method(a);
非公式に言えば、このステートメントはa
、メソッドの最初のパラメータにの値を割り当て、次にメソッドの本体を実行し、メソッドの戻り値をに割り当てることによって評価されresult
ます。最後の例では、プレーンの割り当てと同様に、「右側には、」もしこの文は唯一の有効なことができ、すなわち、「左側」のサブタイプでなければならないtypeof(a) ≤ typeof(parameter(method))
とreturntype(method) ≤ typeof(result)
。つまり、メソッドが次のように宣言されている場合:
Number[] method(ArrayList<Number> list) { ... }
次の式はいずれもコンパイルされません。
Integer[] result = method(new ArrayList<Integer>());
Number[] result = method(new ArrayList<Integer>());
Object[] result = method(new ArrayList<Object>());
だが
Number[] result = method(new ArrayList<Number>());
Object[] result = method(new ArrayList<Number>());
意志。
サブタイプが重要なもう1つの例は、オーバーライドです。考慮してください:
Super sup = new Sub();
Number n = sup.method(1);
どこ
class Super {
Number method(Number n) { ... }
}
class Sub extends Super {
@Override
Number method(Number n);
}
非公式には、ランタイムはこれを次のように書き換えます。
class Super {
Number method(Number n) {
if (this instanceof Sub) {
return ((Sub) this).method(n); // *
} else {
...
}
}
}
マークされた行をコンパイルするには、オーバーライドするメソッドのメソッドパラメーターがオーバーライドされるメソッドのメソッドパラメーターのスーパータイプであり、戻り値の型がオーバーライドされるメソッドのサブタイプである必要があります。正式に言えf(A) = parametertype(method asdeclaredin(A))
ば、少なくとも反変でf(A) = returntype(method asdeclaredin(A))
なければならず、少なくとも共変でなければなりません。
上記の「少なくとも」に注意してください。これらは、静的で型保証された適切なオブジェクト指向プログラミング言語が適用する最小限の要件ですが、プログラミング言語をより厳密にすることもできます。Java 1.4の場合、メソッドをオーバーライドするparametertype(method asdeclaredin(A)) = parametertype(method asdeclaredin(B))
とき、つまりオーバーライドするとき、パラメーターの型とメソッドの戻り値の型は(型の消去を除いて)同一でなければなりません。Java 1.5以降では、オーバーライド時に共変の戻り値の型が許可されています。つまり、以下はJava 1.5ではコンパイルされますが、Java 1.4ではコンパイルされません。
class Collection {
Iterator iterator() { ... }
}
class List extends Collection {
@Override
ListIterator iterator() { ... }
}
私はすべてをカバーしたいと思っています。それでも、抽象、しかし重要な型分散の概念を理解するのに役立つことを願っています。