Kotlin勉強中の私です。Javaから移行組です.
今回、次のようなソースコードを見ていて、疑問に思ったことがいくつかありました。出典は、Android StudioでLoginの機能を自動的に作ってみたところ、Loginの結果を表示するクラスとして、下記のResultクラスが存在しています。
sealed class Result<out T : Any> {
data class Success<out T : Any>(val data: T) : Result<T>()
data class Error(val exception: Exception) : Result<Nothing>()
override fun toString(): String {
return when (this) {
is Success<*> -> "Success[data=$data]"
is Error -> "Error[exception=$exception]"
}
}
}
特に気になったのは
data class Success<out T : Any>(val data: T) : Result<T>()
の部分ですね。
ジェネリクスは、Javaでも使っていたので知ってはいましたが、 out ?? ってなりました。
そこで、初めて「変位指定」とか「型投影」とか「不変」「共変」「反変」という言葉たちに出会いました。
もう、いきなりつまずきそうですよね?!字からして、難しそうで、逃げたくなりますよね??
「共変とはなんぞや?変位指定とはなんぞや!?」
と独眼鉄になっちゃいますね(´ω`)
しかも、説明を読んでも、さっぱりわかりませんでした。
特に、outがスーパータイプを指定することはわかりましたが、Anyって指定すると、なんでもいいわけじゃん?なんで必要なの?となりました。
Kotlin公式サイト ジェネリクス
https://dogwood008.github.io/kotlin-web-site-ja/docs/reference/generics.html
自分でコード書いてみたら、腑に落ちました。(もっと早くやれよ)
class Container<T>(var value:T)
上記のように、Containerクラスを作ります。とってもシンプルなクラスですね。ただ単に、入れ物です。value というプロパティだけがあります。
下記のようなshowというメソッドがあって、引数にContainerを指定します。(Android Studioで実行しています。)
fun show(container:Container<out Any>){
Log.d(TAG, container.toString())
}
show()を呼び出します。
val inta:Container<Int> = Container(23)
show(inta)
val stringa:Container<String> = Container("うきき")
show(stringa)
今、ここでIntもStringもshowの引数として入れられましたね?
では、showメソッドの引数を、次のように引数の型指定から「out」をなくしてみます。
fun show(container:Container<Any>){ //この行からoutがなくなっています。
Log.d(TAG, container.toString())
}
val inta:Container<Int> = Container(23)
show(inta)
val stringa:Container<String> = Container("うきき")
show(stringa)
すると、呼び出し元のshow(inta)、show(stringa)でコンパイルエラーが起こります。
Type mismach.Required Containre<Any>
って出ます。
ああ。つまり、outを指定するのは、Anyのサブタイプならなんでもいいよってためにつけるわけですね。٩( ‘ω’ )و
「ウォーーーーーターーーーー!!!」
とヘレンケラーが叫んだ瞬間もこんな感じだったでしょう。(大げさ)
これが、共変です。
反変はその反対です。(ざっくり)
不変は、outを消したときと同様です。
それにしても共変って日本語わかりにくくないですか??!「共に」「変わる」ってなんだ?オラさっぱりわからなかったぞ!!
初心者なので、理解が間違っているところがあるかもです。(´;ω;`)
ぜひぜひご指摘くださーい。