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 における PECS 原則(Producer Extends, Consumer Super)

Posted at

はじめに

ジェネリクスの「変性(variance)」を理解するとき、
Java ではしばしば PECS 原則 が引用されます。
これは「共変 (out)」「反変 (in)」を直感的に理解する助けになります。


1. PECS 原則とは?

PECS = Producer Extends, Consumer Super

Java のワイルドカード型(? extends T, ? super T)を理解するための経験則であり、
Kotlin の out / in にもそのまま対応します。

英語 意味 Kotlin の対応
Producer Extends 値を「生み出す(出す)」型は extends out
Consumer Super 値を「消費する(受け取る)」型は super in

つまり:

  • 出力側 (Producer) → 共変 (out)
  • 入力側 (Consumer) → 反変 (in)

2. 例:Producer(出力)

データを「生み出す」クラスは 共変 (out) にすべきです。

class Producer<out T>(private val value: T) {
    fun produce(): T = value
}

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

Producer<String> は「String を生み出す」だけなので、
「Any を生み出す」として扱っても安全です。

Java での同義語:

Producer<? extends Any>

3. 例:Consumer(入力)

データを「受け取る」クラスは 反変 (in) にすべきです。

class Consumer<in T> {
    fun consume(value: T) {
        println("Consumed: $value")
    }
}

val numberConsumer: Consumer<Number> = Consumer()
val intConsumer: Consumer<Int> = numberConsumer // OK(反変)

intConsumer.consume(42) // Consumed: 42

Consumer<Number> は「Number(親型)」を受け取れるので、
Consumer<Int>(子型)として扱っても問題ありません。

Java での同義語:

Consumer<? super Int>

4. PECS をイメージで理解する

Producer → out → 出すだけ(読み取り専用)
Consumer → in  → 入れるだけ(書き込み専用)
┌───────────────────────────┐
│  Producer (out)           │
│  ┌──────────────┐        │
│  │  produce():T  │───▶    │
│  └──────────────┘        │
└───────────────────────────┘

┌───────────────────────────┐
│  Consumer (in)            │
│  ◀─── consume(value:T)    │
└───────────────────────────┘

5. 関数型にも当てはまる

Kotlin の関数型 (A) -> B も、PECS の原則そのものです。

位置 役割 変性 キーワード
引数 (A) 消費者 (Consumer) 反変 in
戻り値 (B) 生産者 (Producer) 共変 out
val f: (Number) -> String = { "Number: $it" }
val g: (Int) -> Any = f // OK

引数(in)は NumberInt の方向(反変)
戻り値(out)は StringAny の方向(共変)


6. まとめ:PECS のルールを Kotlin に置き換えると

役割 Java 記法 Kotlin 記法 説明
Producer(出力) ? extends T out T 出すだけ。共変。読み取り専用。
Consumer(入力) ? super T in T 入れるだけ。反変。書き込み専用。

覚え方:

PECS = "Producer Extends, Consumer Super"
Kotlin では “Producer out, Consumer in”


7. 実践例:共変+反変の組み合わせ

interface Transformer<in I, out O> {
    fun transform(input: I): O
}

class StringLengthTransformer : Transformer<String, Int> {
    override fun transform(input: String): Int = input.length
}

val t: Transformer<CharSequence, Number> = StringLengthTransformer()
println(t.transform("Hello")) // 5

I(入力)は in(反変)
O(出力)は out(共変)

つまり、PECS 原則をそのまま型変換に適用しています。


まとめ

キーワード 方向 安全な操作 原則
out 出すだけ Producer<out T> 読み取り専用 Producer Extends
in 入れるだけ Consumer<in T> 書き込み専用 Consumer Super

覚え方の語呂:

「出すなら out、入れるなら in」
「Producer Extends, Consumer Super(PECS)」

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?