0Pricing
Kotlin Academy · レッスン

宣言側変性

inとoutを学びます

「宣言側変性」はCoddyKit上の無料Kotlin Academyレッスンです。 これはレッスン2/4です。 下記で完全なレッスンを無料で読むことができます。その後、ブラウザ内の組み込みコードエディタと24時間対応のAIチューターでハンズオン演習できます。 これはKotlin Academy学習パスの一部であり、ウェブとCoddyKitアプリ全体で進捗が同期されます。 Kotlin Academyコースには全4レッスンが含まれています。

変性の問題

List<String>はList<Any>の一種でしょうか?答えは場合によって異なります。変性とは、型パラメーター同士の関係に応じて、ジェネリック型同士がどのような関係になるかを表すものです。Kotlinでは、これをoutとinで表現します。

デフォルトでは不変

デフォルトでは、ジェネリクスは不変です。StringがAnyのサブタイプであっても、Box<String>はBox<Any>ではありません。これにより、安全でない操作を防げます。

class Box<T>(val value: T)

fun main() {
    val strBox = Box("hi")
    // val anyBox: Box<Any> = strBox // would not compile
    println(strBox.value)
}

out による共変

型パラメーターにoutを付けると共変になり、Producer<String>はProducer<Any>のサブタイプになります。この型パラメーターは出力位置(戻り値の型)にのみ現れます。

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

fun main() {
    val strProducer: Producer<String> = Producer("hello")
    val anyProducer: Producer<Any> = strProducer
    println(anyProducer.produce())
}

out が安全な理由

クラスがTを生成(返却)するだけで、決して受け取らない場合、Producer<String>をProducer<Any>として扱っても安全です。返されるすべてのStringは、同時にAnyでもあるためです。

標準ライブラリでの out

Kotlinの読み取り専用List<out E>は共変です。そのため、List<String>をList<Any>が期待される場所で使用できます。

fun printAll(items: List<Any>) {
    items.forEach { println(it) }
}

fun main() {
    val names: List<String> = listOf("Ann", "Bob")
    printAll(names)
}

in による反変

型パラメーターにinを付けると反変になり、Consumer<Any>はConsumer<String>のサブタイプになります。この型パラメーターは入力位置(パラメーター)にのみ現れます。

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

fun main() {
    val anyConsumer: Consumer<Any> = Consumer()
    val strConsumer: Consumer<String> = anyConsumer
    strConsumer.consume("hi")
}

in が安全な理由

Anyを受け取れるコンシューマーは、当然Stringも受け取れます。そのため、Consumer<Any>はConsumer<String>が必要な場所で使えます。

PECSという覚え方

Producer Extends, Consumer Superと覚えるか、Kotlinの用語では、生成する側(出力)にはout、消費する側(入力)にはinを使うと覚えてください。Tを出力するだけの型にはoutを、Tを入力するだけの型にはinを使います。

in と out の組み合わせ

型パラメーターを入力と出力の両方に使う場合(可変リストなど)は、不変のままにする必要があります。Function型には両方の性質があり、パラメーターはin、戻り値はoutです。

fun main() {
    val f: (Number) -> Int = { it.toInt() }
    val g: (Int) -> Number = f
    println(g(3.0.let { Number::class; 5 }))
}

宣言側変性は1か所で指定する

宣言側変性は、クラス自体にinまたはoutを付けて1度だけ宣言します。その後クラスを使うたびに変性が自動的に適用されます。使用箇所ごとにワイルドカードを繰り返すJavaとは異なります。

in と out の選び方

次のように考えてください。このクラスはTを生成するだけならout、Tを消費するだけならin、両方なら不変のままにします。役割に合った修飾子を選ぶことで、ジェネリック型を柔軟かつ安全にできます。

確認問題

out 修飾子は何を示しますか?

まとめ

ジェネリクスはデフォルトでは不変です。型が値を生成するだけなら共変を表すoutを、値を消費するだけなら反変を表すinを使います。生成する側にはout、消費する側にはinと覚えてください。次は、型プロジェクションを使って使用箇所で変性を適用します。

よくある質問

「宣言側変性」レッスンは無料ですか?

はい。「宣言側変性」の完全なテキストはこのウェブで無料で読めます。インタラクティブに演習し(組み込みコードエディタと24時間対応のAIチューター)、Kotlin Academyコースの残りをアンロックするには、CoddyKit PROにアップグレードしてください。 Kotlin Academyコースには全4レッスンが含まれています。

「宣言側変性」で何を学びますか?

inとoutを学びます ブラウザで直接実行するハンズオンコードでKotlin Academyを演習し、24時間対応のAIチューターがレッスンを進める中での質問に答えます。

Kotlin Academyを始めるのに経験は必要ですか?

事前経験は必要ありません。CoddyKitのKotlin Academyは初級者から上級者向けに構成されているため、ここから始めるか最初から始めて、自分のペースで進むことができます。 これはレッスン2/4です。

「宣言側変性」レッスンにはどのくらい時間がかかりますか?

ほとんどのCoddyKitレッスンは約5~10分かかります。各レッスンはコンパクトでインタラクティブなので、着実に進歩し、ウェブとアプリ全体で正確に前回の場所から再開できます。

このKotlin Academyレッスンでコードを書いて実行できますか?

はい。すべてのKotlin Academyレッスンに組み込みコードエディタが含まれているため、ブラウザでリアルコードを書いて実行し、即座のAIフィードバックを取得できます。ローカル設定は不要です。

このコースのすべてのレッスン

  1. ジェネリック関数とクラス
  2. 宣言側変性
  3. 使用側変性
  4. ジェネリック制約
← Kotlin Academyに戻る