ジェネリッククラス
CoddyのKotlinジャーニー「オブジェクト指向プログラミング」セクションの一部。レッスン 38/57。
クラスにも型パラメーターを持たせることができます。Box<T>は任意の型の値を保持し、各インスタンスで型が固定されます。つまり、Box<Int>はIntの値のみ受け入れます。
class Box<T>(var value: T) {
fun describe() = "box with $value"
}mainの中で:
val numberBox = Box(42)
val textBox = Box("hello")
numberBox.value += 1
println(numberBox.describe())
println(textBox.describe())
// numberBox.value = "x" // エラー: String は Int ではない出力:
box with 43
box with helloコンテナは、典型的なジェネリッククラスです。1つの Stack<T> 実装ですべての要素型に対応でき、コンパイラーが何を入れ、何が取り出されるかをチェックします:
class Stack<T> {
private val items = mutableListOf<T>()
val size: Int
get() = items.size
fun push(item: T) { items.add(item) }
fun pop(): T? = items.removeLastOrNull()
fun peek(): T? = items.lastOrNull()
}main の中では:
val stack = Stack<String>()
stack.push("a")
stack.push("b")
println(stack.pop())
println(stack.peek())
println(stack.size)出力:
b
a
1型引数は、Box(42)のようにコンストラクター引数がある場合はそこから推論されます。それ以外の場合は明示的に記述します: Stack<String>()。異なる型引数を持つインスタンスは異なる型なので、Stack<String>がStack<Int>と混同されることは決してありません。ジェネリッククラスの関数は、それ自身の型パラメーターを追加することもできます:
class Box<T>(val value: T) {
fun <R> map(f: (T) -> R): Box<R> = Box(f(value))
}mainの内部:
val length = Box("kotlin").map { it.length }
println(length.value + 1)出力:
7ジェネリックインターフェースも同じルールに従います。クラスは、ジェネリックな型として実装することも、特定の型引数に対して実装することもできます。
interface Repository<T> {
fun add(item: T)
fun all(): List<T>
}
class MemoryRepository<T> : Repository<T> {
private val items = mutableListOf<T>()
override fun add(item: T) { items.add(item) }
override fun all() = items.toList()
}
class TagRepository : Repository<String> {
private val tags = sortedSetOf<String>()
override fun add(item: String) { tags.add(item.lowercase()) }
override fun all() = tags.toList()
}mainの内部では:
val ids = MemoryRepository<Int>()
ids.add(3); ids.add(1)
val tags = TagRepository()
tags.add("Kotlin"); tags.add("JVM")
println("${ids.all()} ${tags.all()}")出力:
[3, 1] [jvm, kotlin]チャレンジ
簡単最後の limit 個の項目を記憶するジェネリッククラス History<T>(limit) を作成してください。add(item) は項目を追加し、limit 個を超えた場合は最も古い項目を削除します。latest は最も新しい項目、または null です。all() は最も古い項目から最も新しい項目までを、読み取り専用リストとして返します。次に、数値の履歴にのみ存在する拡張 History<Int>.average() を作成してください。これは保持されている数値の整数平均を返します(空の場合は 0)。
提供されたコードは、上限数、単語の行、数値の行を読み取ります。History<String> と History<Int> に値を格納し、保持された単語、最新の単語、保持された数値、それらの平均、および empty latest: null を出力します。
コードは History.kt に記述してください。Main.kt には提供された入出力コードが含まれており、編集できません。
自分で試してみよう
fun main() {
// 提供された入出力コード: そのままにしておく
val input = generateSequence(::readLine).toList()
val limit = input[0].toInt()
val words = History<String>(limit)
for (w in input[1].split(" ")) words.add(w)
val numbers = History<Int>(limit)
for (n in input[2].split(" ")) numbers.add(n.toInt())
println("words: ${words.all()}")
println("latest word: ${words.latest}")
println("numbers: ${numbers.all()}")
println("average: ${numbers.average()}")
println("empty latest: ${History<Char>(3).latest}")
}
このレッスンには短いクイズがあります。レッスンを始めて解答し、進捗を記録しましょう。
オブジェクト指向プログラミングのすべてのレッスン
自分で練習してみよう: Kotlinプレイグラウンド