Menu
Coddy logo textTech

Abstrakte Klassen

Teil des Abschnitts Objektorientierte Programmierung der Kotlin-Journey von Coddy. Lektion 20 von 57.

Eine abstract-Klasse ist eine Oberklasse, die nicht eigenständig instanziiert werden kann. Sie kann abstract-Member ohne Rumpf deklarieren, und jede konkrete Unterklasse muss sie implementieren:

abstract class Shape(val name: String) {
    abstract fun area(): Double

    fun describe() = "$name with area ${area()}"
}
class Circle(val r: Double) : Shape("circle") {
    override fun area() = 3.14 * r * r
}
class Rect(val w: Double, val h: Double) : Shape("rect") {
    override fun area() = w * h
}

Innerhalb von main:

val shapes: List<Shape> = listOf(Circle(1.0), Rect(2.0, 3.0))
for (s in shapes) println(s.describe())

Ausgabe:

circle with area 3.14
rect with area 6.0

Eine abstrakte Klasse ist ohne das Schlüsselwort open, und auch ihre abstrakten Member sind open, sodass Unterklassen nur override schreiben. Das Erstellen der abstrakten Klasse selbst lässt sich nicht kompilieren, und eine Unterklasse, die ein abstraktes Member auslässt, muss ebenfalls abstrakt sein. Verwende eine abstrakte Klasse, wenn die Oberklasse für einige Member kein sinnvolles eigenes Verhalten hat:

// val s = Shape("blob")                       // Fehler: kann keine Instanz einer abstrakten Klasse erstellen
// class Blob : Shape("blob")                  // Fehler: Blob implementiert area() nicht
abstract class Polygon(name: String) : Shape(name)   // in Ordnung: immer noch abstrakt

Eine abstrakte Klasse kann einen Konstruktor, einen Zustand und normale Methoden haben, die Unterklassen erben. Eine häufige Verwendung ist ein festgelegter Ablauf, dessen Schritte variieren, die Template-Methode: Die Methode der Oberklasse führt die Schritte aus, und die Unterklassen füllen die abstrakten Schritte aus:

abstract class Report(val title: String) {
    fun render() = "== $title ==\n${body()}\n(${footer()})"

    abstract fun body(): String
    open fun footer() = "end"
}
class Sales(val total: Int) : Report("Sales") {
    override fun body() = "total: $total"
}
class Stock(val items: Int) : Report("Stock") {
    override fun body() = "items: $items"
    override fun footer() = "counted today"
}

Innerhalb von main:

println(Sales(420).render())
println(Stock(17).render())

Ausgabe:

== Sales ==
total: 420
(end)
== Stock ==
items: 17
(counted today)

Abstrakte Eigenschaften funktionieren auf dieselbe Weise. Die Unterklasse stellt den Wert als Konstruktor-Eigenschaft, gespeicherte Eigenschaft oder Getter bereit:

abstract class Worker(val name: String) {
    abstract val monthly: Int
    fun yearly() = monthly * 12
}
class Salaried(name: String, override val monthly: Int) : Worker(name)
class Hourly(name: String, val hours: Int, val rate: Int) : Worker(name) {
    override val monthly: Int
        get() = hours * rate
}

Innerhalb von main:

val staff = listOf(Salaried("Ada", 4000), Hourly("Bo", 80, 25))
for (w in staff) println("${w.name}: ${w.yearly()}")

Ausgabe:

Ada: 48000
Bo: 24000
challenge icon

Aufgabe

Einfach

Discount ist eine abstrakte Klasse. Sie deklariert die abstrakte Methode amount(price) und implementiert apply(price), die den Preis minus amount(price) zurückgibt, jedoch niemals weniger als 0. Vervollständige die Unterklassen: PercentOff(percent) nimmt diesen Prozentsatz des Preises (abgerundet), FixedOff(value) nimmt einen festen Wert, und SpendMore(threshold, off) nimmt off nur dann, wenn der Preis mindestens threshold beträgt.

Der bereitgestellte Code liest den Preis ein und wendet anschließend p,10, f,300 oder s,500,50 als Rabatt an, wendet jeden davon auf den Preis an und gibt 1000 -> 900 aus.

Dein Code gehört in Discount.kt, PercentOff.kt, FixedOff.kt und SpendMore.kt. Main.kt enthält den bereitgestellten Eingabe-/Ausgabecode und kann nicht bearbeitet werden.

Probier es selbst

fun main() {
    // Bereitgestellter Ein-/Ausgabecode: belasse ihn wie er ist
    val input = generateSequence(::readLine).toList()
    val price = input[0].toInt()
    for (line in input.drop(1)) {
        val p = line.split(",")
        val discount: Discount = when (p[0]) {
            "p" -> PercentOff(p[1].toInt())
            "f" -> FixedOff(p[1].toInt())
            else -> SpendMore(p[1].toInt(), p[2].toInt())
        }
        println("$price -> ${discount.apply(price)}")
    }
}
quiz iconTeste dich selbst

Diese Lektion enthält ein kurzes Quiz. Starte die Lektion, um es zu beantworten und deinen Fortschritt zu speichern.

Alle Lektionen in Objektorientierte Programmierung

Übe selbstständig: Kotlin-Playground