Klasy generyczne
Część sekcji Programowanie obiektowe ścieżki Kotlin w Coddy. Lekcja 38 z 57.
Klasa również może mieć parametry typu. Box<T> przechowuje wartość dowolnego typu, a każda instancja ustala typ: Box<Int> akceptuje tylko wartości typu Int:
class Box<T>(var value: T) {
fun describe() = "box with $value"
}Wewnątrz main:
val numberBox = Box(42)
val textBox = Box("hello")
numberBox.value += 1
println(numberBox.describe())
println(textBox.describe())
// numberBox.value = "x" // błąd: String nie jest typem IntWynik:
box with 43
box with helloKontenery to typowe klasy generyczne. Jedna implementacja Stack<T> działa dla każdego typu elementu, a kompilator sprawdza, co trafia do środka i co z niego wychodzi:
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()
}Wewnątrz main:
val stack = Stack<String>()
stack.push("a")
stack.push("b")
println(stack.pop())
println(stack.peek())
println(stack.size)Wynik:
b
a
1Argument typu jest wnioskowany z argumentów konstruktora, jeśli jakieś są, jak w przypadku Box(42). W przeciwnym razie podaj go: Stack<String>(). Instancje z różnymi argumentami typu są różnymi typami, więc Stack<String> nigdy nie zostanie pomylony z Stack<Int>. Funkcje klasy generycznej mogą również definiować własne parametry typu:
class Box<T>(val value: T) {
fun <R> map(f: (T) -> R): Box<R> = Box(f(value))
}Wewnątrz main:
val length = Box("kotlin").map { it.length }
println(length.value + 1)Wynik:
7Interfejsy generyczne podlegają tym samym regułom. Klasa może implementować jeden generycznie lub dla jednego konkretnego argumentu typu:
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()
}Wewnątrz 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()}")Wynik:
[3, 1] [jvm, kotlin]Wyzwanie
ŁatwyNapisz klasę generyczną History<T>(limit), która zapamiętuje ostatnie elementy limit. add(item) dodaje element na końcu i usuwa najstarszy, gdy jest ich więcej niż limit. latest to najnowszy element albo null, a all() zwraca elementy od najstarszego do najnowszego w postaci listy tylko do odczytu. Następnie napisz rozszerzenie History<Int>.average(), które istnieje tylko dla historii liczb i zwraca średnią całkowitą przechowywanych liczb (0, gdy historia jest pusta).
Dostarczony kod odczytuje limit, wiersz słów i wiersz liczb. Wypełnia obiekty History<String> i History<Int>, a następnie wypisuje przechowywane słowa, najnowsze słowo, przechowywane liczby, ich średnią oraz empty latest: null.
Umieść swój kod w pliku History.kt. Plik Main.kt zawiera dostarczony kod wejścia/wyjścia i nie można go edytować.
Spróbuj swoich sił
fun main() {
// Dostarczony kod wejścia/wyjścia: pozostaw go bez zmian
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}")
}
Ta lekcja zawiera krótki quiz. Zacznij lekcję, żeby na niego odpowiedzieć i śledzić swoje postępy.
Wszystkie lekcje w sekcji Programowanie obiektowe
1Klasy i obiekty
Praca z plikamiDefiniowanie klasKonstruktory główneBloki init i walidacjaKonstruktory dodatkowePodsumowanie — karta biblioteczna4Dziedziczenie
Klasy otwarte i podklasyNadpisywanie składowychsuper i konstruktoryKlasy abstrakcyjnePodsumowanie – katalog pracowników7Klasy specjalne
Klasy danychKlasy wyliczenioweHierarchie zapieczętowaneequals, hashCode, toStringPodsumowanie — śledzenie zamówień10Wzorce projektowe, część 2
Delegowanie za pomocą byWzorzec dekoratoraWzorzec budowniczegoWzorzec stanu2Właściwości i hermetyzacja
Własne akcesoryModyfikatory widocznościPrywatne setterylazy i lateinitPowtórka – konto bankowe5Interfejsy
Deklarowanie interfejsówMetody domyślneWiele interfejsówInterfejsy jako typyPowtórzenie – metody płatności8Typy generyczne
Funkcje generyczneKlasy generyczneOgraniczenia typówKowariancja i kontrawariancja: in i outPodsumowanie — kolejka priorytetowa11Projekt: Zarządzanie biblioteką
Książki i czytelnicyWypożyczanie książekPoćwicz samodzielnie: Edytor online Kotlin