Caution
お使いのブラウザはJavaScriptが実行できない状態になっております。
当サイトはWebプログラミングの情報サイトの為、
JavaScriptが実行できない環境では正しいコンテンツが提供出来ません。
JavaScriptが実行可能な状態でご閲覧頂くようお願い申し上げます。
共変 / 反変
Kotlinのジェネリクスで『out』(共変)を付けると生産者として読み取り専用に、『in』(反変)を付けると消費者として書き込み専用になります。型の安全な上位互換・下位互換を実現します。
構文
// 共変(out) — T の上位型として扱える(読み取り専用・Producer)
class Producer<out T>(val value: T) {
fun get(): T = value // OK: T を返せます
// fun set(v: T) { } // NG: T を引数に取れません
}
// 反変(in) — T の下位型として扱える(書き込み専用・Consumer)
class Consumer<in T> {
fun consume(value: T) { println(value) } // OK: T を引数に取れます
// fun get(): T { } // NG: T を返せません
}
構文一覧
| キーワード | 呼び方 | 概要 |
|---|---|---|
| out T | 共変(Covariant) | T の上位型に代入可。T は返り値のみに使えます(Producer)。 |
| in T | 反変(Contravariant) | T の下位型に代入可。T は引数のみに使えます(Consumer)。 |
| T(変位なし) | 不変(Invariant) | 上位・下位型への代入不可。読み書き両方で使えます。 |
| List<out T> | 使用箇所変位 | Kotlin 標準ライブラリでの共変の例です。 |
サンプルコード
// 共変(out)の例 — Animal の上位型として扱えます。
open class Animal(val name: String)
class Dog(name: String) : Animal(name)
class Cat(name: String) : Animal(name)
class Cage<out T : Animal>(val animal: T) {
fun getAnimal(): T = animal
}
// 反変(in)の例 — Animal の下位型として扱えます。
interface Trainer<in T : Animal> {
fun train(animal: T)
}
fun main() {
// 共変: Cage<Dog> を Cage<Animal> として扱えます。
val dogCage: Cage<Dog> = Cage(Dog("Pochi"))
val animalCage: Cage<Animal> = dogCage // out があるので代入OK
println(animalCage.getAnimal().name) // Pochi
// 反変: Trainer<Animal> を Trainer<Dog> として扱えます。
val animalTrainer: Trainer<Animal> = object : Trainer<Animal> {
override fun train(animal: Animal) {
println("${animal.name} を訓練します")
}
}
val dogTrainer: Trainer<Dog> = animalTrainer // in があるので代入OK
dogTrainer.train(Dog("Koro")) // Koro を訓練します
// Kotlin 標準の List は out T(共変)
val dogs: List<Dog> = listOf(Dog("A"), Dog("B"))
val animals: List<Animal> = dogs // OK
println(animals.map { it.name }) // [A, B]
}
概要
変位指定のないジェネリクス(不変)は最も制限が厳しく、『Box<Dog>』を『Box<Animal>』に代入できません。『out』を付けた共変クラスは型を読み取るだけの「生産者」、『in』を付けた反変クラスは型を消費するだけの「消費者」として機能します。
Kotlin の標準ライブラリでは『List<out E>』が共変の代表例です。読み取り専用リストなので上位型に代入できます。一方『MutableList<E>』は不変のため代入できません。
ジェネリクスの基本はジェネリクス — 基本を、実行時の型情報保持はreified 型パラメーターを参照してください。
記事の間違いや著作権の侵害等ございましたらお手数ですがこちらまでご連絡頂ければ幸いです。