共変反変についてのお勉強。備忘録です。
Kotlin Programming Language(日本語訳) を読んでいたのですが、何度読んでも共変反変がぼやっとしかわからない;;
ということで忘れないように参考にしたサイトと考えたことを残しておきます。
共変と反変
共変と反変はkotlinではinとout修飾子で表します。inが反変、outが共変です。
[追記]Javaでは extends Object>みたいな感じで書かないといけない。?が何の継承かを明記しなければならないのだと思います。
Kotlin Programming Language(日本語訳)では以下のように言い換えています。
実存的言い換え:コンシューマ(消費者)は in、プロデューサ(生産者)は out !
これはin(反変)は消費だけする(引数として使用)、out(共変)は提供だけする(戻り値として使用)ということだと理解しました。
そもそも共変と反変とは何か。
例えばAny型とString型があったとして、AnyはStringのスーパータイプという関係があります。
ではList<Any>とList<String>にはどんな関係があるか...通常は何もありません。
この状態を不変と言いますが、これに関係を持たせるのが共変と反変です(と理解しました)。
共変だとList<Any>はList<String>のスーパータイプで、反変だとList<String>はList<Any>のスーパータイプとなります。
詳しい解説ができるほど勉強できていないので、参考にしたサイトを最後に置いておきます。
お試しコード
とりあえずどんな特性を持つのかをコードを書いて試してみました。
Try Kotlinを使ったのでまだgithubに上げていません。のちに上げます。
[追記]あげました。
https://github.com/raibito/KotlinCodeBowl/blob/master/covariance_contravariance.kt
// 共変 -> プロデューサ 提供(戻り値に使う)だけする、消費(引数として使用)できない
class MyListOut<out T>(value: T) {
val value: T= value
get() = field // valはgetだけ持つ(提供だけする)ため、共変
}
// 反変 -> コンシューマ 消費(引数として使用)だけする、提供(戻り値に使う)できない
class MyListIn<in T>{
fun print(value :T){ // valもvarもget(提供)は持つため、Tは引数としてしか使えない
println(value)
}
}
fun main(args: Array<String>) {
// 共変 out
val anyList1: MyListOut<Any> = MyListOut<String>("test1")
val any: Any = anyList1.value // 提供はOK
println(any)
// 反変 in
val anyList2: MyListIn<String> = MyListIn<Any>()
anyList2.print("test2") // 消費はOK
}
基本的に今回わかったことにはコメントをつけています。
うまくまとまってなくて申し訳ないです。
参考
Kotlin Programming Language(日本語訳)
https://dogwood008.github.io/kotlin-web-site-ja/docs/reference/generics.html
【Java】ジェネリックス型の不変、共変、反変とは何か
http://www.thekingsmuseum.info/entry/2016/02/07/235454