Menu
Coddy logo textTech

Klasy abstrakcyjne

Część sekcji Programowanie obiektowe ścieżki Kotlin w Coddy. Lekcja 20 z 57.

Klasa abstract jest nadklasą, której nie można samodzielnie utworzyć jako instancji. Może deklarować składowe abstract bez treści, a każda konkretna podklasa musi je zaimplementować:

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
}

Wewnątrz main:

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

Wynik:

circle with area 3.14
rect with area 6.0

Klasa abstrakcyjna jest otwarta bez słowa kluczowego, a jej abstrakcyjne składowe również są otwarte, więc podklasy zapisują tylko override. Utworzenie samej klasy abstrakcyjnej nie kompiluje się, a podklasa, która pomija abstrakcyjną składową, również musi być abstrakcyjna. Użyj klasy abstrakcyjnej, gdy nadklasa nie ma sensownego własnego zachowania dla niektórych składowych:

// val s = Shape("blob")                       // błąd: nie można utworzyć instancji klasy abstrakcyjnej
// class Blob : Shape("blob")                  // błąd: Blob nie implementuje area()
abstract class Polygon(name: String) : Shape(name)   // poprawne: nadal abstrakcyjna

Klasa abstrakcyjna może mieć konstruktor, stan i zwykłe metody, które dziedziczą podklasy. Częstym zastosowaniem jest stała procedura, której kroki się różnią — metoda szablonowa: metoda nadklasy wykonuje kroki, a podklasy uzupełniają abstrakcyjne:

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"
}

Wewnątrz main:

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

Wynik:

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

Właściwości abstrakcyjne działają tak samo. Podklasa dostarcza wartość jako właściwość konstruktora, przechowywaną właściwość lub getter:

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
}

Wewnątrz main:

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

Wynik:

Ada: 48000
Bo: 24000
challenge icon

Wyzwanie

Łatwy

Discount jest klasą abstrakcyjną. Deklaruje abstrakcyjną metodę amount(price) i implementuje apply(price), która zwraca cenę pomniejszoną o amount(price), ale nigdy mniej niż 0. Uzupełnij podklasy: PercentOff(percent) odejmuje podany procent ceny (zaokrąglony w dół), FixedOff(value) odejmuje stałą wartość, a SpendMore(threshold, off) odejmuje off tylko wtedy, gdy cena wynosi co najmniej threshold.

Dostarczony kod odczytuje cenę, a następnie rabaty p,10, f,300 lub s,500,50, stosuje każdy z nich do ceny i wypisuje 1000 -> 900.

Twój kod umieść w plikach Discount.kt, PercentOff.kt, FixedOff.kt i SpendMore.kt. 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 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 iconSprawdź się

Ta lekcja zawiera krótki quiz. Zacznij lekcję, żeby na niego odpowiedzieć i śledzić swoje postępy.

Wszystkie lekcje w sekcji Programowanie obiektowe

Poćwicz samodzielnie: Edytor online Kotlin