Classi generiche
Fa parte della sezione Programmazione orientata agli oggetti del percorso Kotlin di Coddy. Lezione 38 di 57.
Anche una classe può avere parametri di tipo. Box<T> contiene un valore di qualsiasi tipo e ogni istanza fissa il tipo: un Box<Int> accetta solo valori Int:
class Box<T>(var value: T) {
fun describe() = "box with $value"
}All'interno di main:
val numberBox = Box(42)
val textBox = Box("hello")
numberBox.value += 1
println(numberBox.describe())
println(textBox.describe())
// numberBox.value = "x" // error: un String no es un IntOutput:
box with 43
box with helloI contenitori sono le classi generiche più comuni. Un'implementazione di Stack<T> funziona per ogni tipo di elemento e il compilatore controlla cosa viene inserito e cosa restituito:
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()
}All'interno di main:
val stack = Stack<String>()
stack.push("a")
stack.push("b")
println(stack.pop())
println(stack.peek())
println(stack.size)Output:
b
a
1L'argomento di tipo viene dedotto dagli argomenti del costruttore, se presenti, come in Box(42). Altrimenti, specificalo: Stack<String>(). Le istanze con argomenti di tipo diversi sono tipi diversi, quindi una Stack<String> non viene mai confusa con una Stack<Int>. Anche le funzioni di una classe generica possono aggiungere parametri di tipo propri:
class Box<T>(val value: T) {
fun <R> map(f: (T) -> R): Box<R> = Box(f(value))
}All'interno di main:
val length = Box("kotlin").map { it.length }
println(length.value + 1)Output:
7Le interfacce generiche seguono le stesse regole. Una classe può implementarne una in modo generico oppure per un particolare argomento di tipo:
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()
}Dentro 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()}")Output:
[3, 1] [jvm, kotlin]Sfida
FacileScrivi la classe generica History<T>(limit) che memorizza gli ultimi limit elementi. add(item) aggiunge un elemento e rimuove il più vecchio quando ce ne sono più di limit. latest è l'elemento più recente oppure null, e all() restituisce gli elementi dal più vecchio al più recente come lista di sola lettura. Poi scrivi l'estensione History<Int>.average(), che esiste solo per gli archivi di numeri e restituisce la media intera dei numeri conservati (0 se è vuoto).
Il codice fornito legge il limite, una riga di parole e una riga di numeri. Riempie un History<String> e un History<Int> e stampa le parole conservate, la parola più recente, i numeri conservati, la loro media e empty latest: null.
Il tuo codice va in History.kt. Main.kt contiene il codice di input/output fornito e non può essere modificato.
Provalo tu
fun main() {
// Codice di input/output fornito: lascialo così com'è
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}")
}
Questa lezione include un breve quiz. Inizia la lezione per rispondere e tenere traccia dei tuoi progressi.
Tutte le lezioni di Programmazione orientata agli oggetti
1Classi e oggetti
Lavorare con i fileDefinire le classiCostruttori primariBlocchi init e convalidaCostruttori secondariRiepilogo - Tessera della biblioteca4Ereditarietà
Classi aperte e sottoclassiRidefinizione dei membrisuper e costruttoriClassi astratteRiepilogo - Elenco del personale2Proprietà e incapsulamento
Accessor personalizzatiModificatori di visibilitàSetter privatilazy e lateinitRipasso - Conto bancario5Interfacce
Dichiarare le interfacceMetodi predefinitiInterfacce multipleLe interfacce come tipiRiepilogo - Metodi di pagamento8Tipi generici
Funzioni genericheClassi genericheVincoli sui tipiVarianza con in e outRiepilogo - Coda con priorità11Progetto: Gestione della biblioteca
Libri e sociPrendere in prestito i libriEsercitati da solo: Playground Kotlin