【きょうへんせいとはんへんせい】

共変性と反変性 とは?

最終更新:
💡 猫を返す側と、動物を受け取る側の違い

型Aが型Bとして使えるとき、その関係が型を組み合わせた形でも同じ向きに伝わるか、逆向きになるかを表す性質。値を返す側と受け取る側で、安全な置き換えの向きが変わる。

📌 このページのポイント
型の置き換え:返す側と受け取る側猫(Cat)は動物(Animal)の一種共変:猫を返せる → 動物を返せるProducer<Cat>猫を返すProducer<Animal>動物を返すKotlin:Producer<out T>を宣言した例反変:動物を受ける → 猫を受けるConsumer<Animal>動物全般に対応Consumer<Cat>猫にも対応Kotlin:Consumer<in T>を宣言した例矢印:左を、右の型として使える向き
共変・反変を宣言した型の例。不変の型では、CatとAnimalの関係だけで同様に置き換えられません。
ひよこ ひよこ
CatはAnimalなのに、猫のリストを動物のリストにできないの?
ペンギン先生 ペンギン先生
読み書きするリストを自由に置き換えると危険だよ。動物のリストとして犬を追加できると、猫だけのはずのリストに犬が入るからね。JavaのListは、このような置き換えを型引数の継承関係だけでは認めない、不変の型なんだ。
ひよこ ひよこ
共変性は、どっち向き?
ペンギン先生 ペンギン先生
猫を返すProducer<Cat>を、動物を返すProducer<Animal>として使う向きだよ。猫は動物だから、受け取る側の期待を満たせる。元のCatからAnimalへの関係と同じ向きなんだ。Kotlinでは、その型引数を出力位置で使う型にoutを宣言できるよ。
ひよこ ひよこ
反変性は、なぜ逆向きなの?
ペンギン先生 ペンギン先生
動物を受け取れるConsumer<Animal>は、猫を受け取るConsumer<Cat>としても使えるよ。動物全般に対応できるなら、猫にも対応できるからね。型を組み合わせると、元の関係とは逆向きになる。Kotlinでは入力位置の型引数にinを宣言できるんだ。
ひよこ ひよこ
読み取り専用なら、絶対に中身は変わらない?
ペンギン先生 ペンギン先生
それは別の話だよ。たとえばJavaの? extends Animalでは、型の分からない要素を追加できないけれど、clearなど別の操作まで全部禁止されるわけではない。安全な型の置き換えと、オブジェクトが変更されない不変性は区別しよう。
ひよこ ひよこ
言語が変わっても、同じ書き方?
ペンギン先生 ペンギン先生
違うよ。Kotlinの宣言時のin・out、Javaの利用時の? extendsや? superなど、表現方法がある。型が使われる位置やAPIの宣言を確認しよう。図は共変のProducerと反変のConsumerを宣言した場合の例で、すべての型が自動でそうなるわけではないんだ。
ペンギン
まとめ:ざっくりこれだけ覚えればOK!
「共変性と反変性」って出てきたら「型を組み合わせたとき、安全に置き換えられる向きのルール」と思えばだいたいOK!
📖 おまけ:英語の意味
「Covariance and Contravariance」 = 共変性と反変性
💬 co- は「一緒に」contra- は「反対に」という意味の接頭辞で、型パラメータの変化が元の型の継承方向と同じか逆かを表しているんだよ。

参考資料

← 用語集にもどる