[解決済み] 共分散、不変化、共分散をわかりやすく解説?
質問
今日、私はJavaの共分散、共分散(および不変量)についてのいくつかの記事を読みました。私は英語とドイツ語の Wikipedia の記事、および IBM からのいくつかの他のブログ投稿と記事を読みました。
しかし、これらが正確に何についてなのか、私はまだ少し混乱しています。ある人は型とサブタイプの関係について、ある人は型変換について、ある人はメソッドがオーバーライドされるかオーバーロードされるかを決定するために使用されると言います。
そこで、CovarianceとContravariance(とInvariance)が何なのか、初心者にわかりやすく説明するものを探しています。簡単な例のためのプラスポイント。
どのように解決するのですか?
<ブロッククオート型とサブタイプの関係であるとか、型変換であるとか、メソッドが上書きされるかオーバーロードされるかの判断に使われるとか、いろいろ言われているようです。
上記のすべてです。
要するに、これらの用語はサブタイプ関係が型変換によってどのように影響されるかを説明しています。つまり、もし
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
は共変、共変、不変のどれでしょうか?共変とは
List<String>
のサブタイプであることを意味します。
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];
になります。
サブタイプの関係が問題となるもう一つの例は、メソッド呼び出し式です。
result = method(a);
非公式に言えば、この文は
a
の値をメソッドの最初のパラメータに代入し、 メソッドの本体を実行し、メソッドの戻り値を
result
. 最後の例での単純な代入と同様に、"右手側" は"左手側" のサブタイプでなければなりません。
typeof(a) ≤ typeof(parameter(method))
と
returntype(method) ≤ typeof(result)
. つまり、method がによって宣言されている場合。
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>());
になります。
サブタイトリングが問題となるもう一つの例はオーバーライドです。考えてみてください。
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))
は少なくとも共変でなければなりません。
上記の "at least" に注意してください。これらは、合理的な静的型安全オブジェクト指向プログラミング言語が強制する最低限の要件ですが、プログラミング言語はより厳しくすることを選択することができます。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() { ... }
}
私はすべてをカバーしたことを願っています - というより、表面を掻いただけです。それでも、抽象的ではありますが、型分散の重要な概念を理解する助けになればと思います。
関連
-
スキャナは、タイプに解決することはできません最もルーキー初心者の質問
-
エラーの解決方法 jarfile XXX.jarにアクセスできません。
-
Methodのinvokeメソッド実装のJavaリフレクション
-
スレッド "main" での例外 java.lang.ArrayIndexOutOfBoundsException: 1
-
spring-boot 401 このリソースにアクセスするには完全な認証が必要です エラー解決
-
javaコンパイル時のエラー:不正な文字 '\ufeff' に対する解決策です。
-
自動配線された依存性のインジェクションに失敗しました。
-
コミットには何も追加されないが、未追跡のファイルが存在し、gitで未追跡のファイルに対する完璧な解決策
-
[解決済み] 共分散と共分散の実世界の例
-
[解決済み] 共分散と共分散が値型をサポートしない理由
最新
-
nginxです。[emerg] 0.0.0.0:80 への bind() に失敗しました (98: アドレスは既に使用中です)
-
htmlページでギリシャ文字を使うには
-
ピュアhtml+cssでの要素読み込み効果
-
純粋なhtml + cssで五輪を実現するサンプルコード
-
ナビゲーションバー・ドロップダウンメニューのHTML+CSSサンプルコード
-
タイピング効果を実現するピュアhtml+css
-
htmlの選択ボックスのプレースホルダー作成に関する質問
-
html css3 伸縮しない 画像表示効果
-
トップナビゲーションバーメニュー作成用HTML+CSS
-
html+css 実装 サイバーパンク風ボタン
おすすめ
-
アクセス制限について アプリケーションの種類がAPIでない(必要なライブラリの制限)。
-
プロローグでのコンテンツは禁止されています
-
java.lang.NoClassDefFoundError: org.apache.jasper.el.ELContextImpl クラスを初期化できませんでした。
-
Error: java.lang.NoClassDefFoundError: クラス XXXX を初期化できませんでした
-
あるコードに出会いましたが、何に使うのか理解できません。 List<String> list = new ArrayList<String>() { { a
-
Java(1)仕上げの基本概念+eclipseのインストール構成
-
コミットには何も追加されないが、未追跡のファイルが存在し、gitで未追跡のファイルに対する完璧な解決策
-
maven プラグイン エラー プラグインの実行は、ライフサイクル構成ソリューションの対象外です。
-
[解決済み] List<Dog> は List<Animal> のサブクラスですか?Java のジェネリックはなぜ暗黙のうちに多相性にならないのですか?
-
[解決済み] 共分散と共分散の違いについて