Interfaces
interface Sprechend {
val name: String // abstrakte Eigenschaft
fun spricht(): String
fun vorstellen() = "$name: ${spricht()}" // Standardimplementierung
}
interface Laufend { fun laeuft() = "läuft" }
class Mensch(override val name: String) : Sprechend, Laufend {
override fun spricht() = "Hallo"
}
class Roboter : Sprechend {
override val name = "R2"
override fun spricht() = "Piep"
}
fun main() {
val liste: List<Sprechend> = listOf(Mensch("Mia"), Roboter())
liste.forEach { println(it.vorstellen()) }
println(Mensch("Tom").laeuft())
}Ausgabe
Mia: Hallo R2: Piep läuft
Anders als in Java dürfen Interfaces Eigenschaften und Standardmethoden haben. Eine Klasse kann mehrere Interfaces, aber nur eine Oberklasse haben.
Generics
class Kiste<T>(private val inhalt: T) {
fun auspacken(): T = inhalt
}
fun <T> mitte(liste: List<T>): T = liste[liste.size / 2]
fun <T : Comparable<T>> groesstes(liste: List<T>): T { // obere Schranke
var m = liste.first()
for (x in liste) if (x > m) m = x
return m
}
class Stapel<T> {
private val daten = mutableListOf<T>()
fun push(x: T) { daten.add(x) }
fun pop(): T? = if (daten.isEmpty()) null else daten.removeAt(daten.size - 1)
val groesse get() = daten.size
}
fun main() {
println(Kiste("Text").auspacken())
println(Kiste(42).auspacken() + 1)
println(mitte(listOf(1, 2, 3, 4, 5)))
println(groesstes(listOf(3, 9, 4)))
println(groesstes(listOf("kiwi", "apfel")))
val s = Stapel<Int>()
s.push(1); s.push(2)
println("${s.pop()} ${s.pop()} ${s.pop()} ${s.groesse}")
}Ausgabe
Text 43 3 9 kiwi 2 1 null 0
Varianz: out und in
List<out T> ist kovariant: Eine List<String> darf als List<Any> verwendet werden (nur lesen). in ist kontravariant (nur schreiben). Das ist Kotlins sichere Variante der Java-Wildcards:
interface Quelle<out T> { fun hole(): T }
interface Senke<in T> { fun nimm(x: T) }
fun main() {
val strings: List<String> = listOf("a")
val anys: List<Any> = strings // erlaubt wegen "out"
println(anys)
val quelle = object : Quelle<String> { override fun hole() = "daten" }
val q: Quelle<Any> = quelle
println(q.hole())
val senke = object : Senke<Any> { override fun nimm(x: Any) = println("nehme $x") }
val s: Senke<String> = senke
s.nimm("Text")
}Ausgabe
[a] daten nehme Text
Delegation
Klassendelegation mit `by`
interface Drucker { fun drucke(text: String) }
class KonsolenDrucker : Drucker { override fun drucke(text: String) = println("> $text") }
class Bericht(drucker: Drucker) : Drucker by drucker { // delegiert alle Methoden
fun fertig() = drucke("Bericht fertig")
}
fun main() {
Bericht(KonsolenDrucker()).fertig()
}Ausgabe
> Bericht fertig
Eigenschaften-Delegation
import kotlin.properties.Delegates
class Einstellung {
var lautstaerke: Int by Delegates.observable(5) { _, alt, neu ->
println("Lautstärke: $alt -> $neu")
}
var name: String by Delegates.vetoable("x") { _, _, neu -> neu.length <= 5 }
val teuer: Int by lazy { println("berechne"); 42 }
}
fun main() {
val e = Einstellung()
e.lautstaerke = 8
e.name = "kurz"
e.name = "viel zu lang"
println(e.name)
println(e.teuer + e.teuer)
}Ausgabe
Lautstärke: 5 -> 8 kurz berechne 84
Merke
- Interfaces mit Eigenschaften und Standardmethoden, mehrere pro Klasse
- Generics
<T>, Schranken<T : Comparable<T>>, Varianzout/in - Klassendelegation mit
bystatt Vererbung Delegates.observable,vetoableundlazyfür Eigenschaften
Aufgabe
Schreibe eine generische Klasse Paar<A, B> mit Funktion tauschen(), die Paar<B, A> liefert.