0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

【Kotlin】Kotlin の共変 (out) と反変 (in)

Posted at

はじめに

ジェネリクスを使うとき、型パラメータの「代入可能性(互換性)」をどう扱うかが問題になります。
Kotlin ではこれを 変性(variance) と呼び、outin キーワードで制御します。


1. なぜ変性が必要?

次の例を考えてみます:

val strings: List<String> = listOf("a", "b")
// val anys: List<Any> = strings // エラー!

一見「StringAny のサブタイプだから代入できるのでは?」と思えますが、
もし List<Any>42 を追加できてしまったら、元は List<String> なので破綻してしまいます。

このような 不整合を防ぐために、デフォルトでは代入できない のです。
そこで Kotlin では outin を使って安全な代入関係を表現します。


2. 共変 (out)

定義

  • out型パラメータが「出力専用(読み取り専用)」 であることを示す。
  • Producer<out T> のように宣言すると、T を生成する側(返すだけ) に使える。

class Producer<out T>(private val value: T) {
    fun produce(): T = value
    // fun consume(item: T) {} // エラー:in 引数には使えない
}

val stringProducer: Producer<String> = Producer("Hello")
val anyProducer: Producer<Any> = stringProducer // 代入可能(共変)
println(anyProducer.produce()) // Hello

Producer<String>Producer<Any> として扱える
なぜなら「出すだけ」なので Any として読んでも安全だからです。


3. 反変 (in)

定義

  • in型パラメータが「入力専用(書き込み専用)」 であることを示す。
  • Consumer<in T> のように宣言すると、T を受け取る側(引数として消費) に使える。

class Consumer<in T> {
    fun consume(item: T) {
        println("Consumed: $item")
    }
    // fun produce(): T {} // エラー:戻り値には使えない
}

val numberConsumer: Consumer<Number> = Consumer()
val intConsumer: Consumer<Int> = numberConsumer // 代入可能(反変)

intConsumer.consume(10) // Consumed: 10

Consumer<Number>Consumer<Int> として扱える
なぜなら「消費するだけ」なので、Int を渡しても安全だからです。


4. イメージ図

共変(out)   : サブタイプ関係をそのまま維持
   Producer<String>  →  Producer<Any>

反変(in)    : サブタイプ関係が逆になる
   Consumer<Number>  →  Consumer<Int>

5. 実用例:関数型の変性

関数型 (A) -> B は次のルールで変性が自動的に付与されています:

  • 引数(入力) → in
  • 戻り値(出力) → out
val f: (Number) -> String = { "Number: $it" }
val g: (Int) -> Any = f // OK

Kotlin の関数型は 入力は反変・出力は共変 という仕組みになっています。


6. まとめ

  • 共変 (out)
    • 出力専用(返すだけ)
    • Producer<out T>
    • Producer<String>Producer<Any> に代入できる
  • 反変 (in)
    • 入力専用(受け取るだけ)
    • Consumer<in T>
    • Consumer<Number>Consumer<Int> に代入できる
  • 関数型は自動的に
    • 引数 → in(反変)
    • 戻り値 → out(共変)

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?