abstract în Kotlin

abstract marchează o clasă care nu poate fi creată direct și membri fără implementare, pe care subclasele trebuie să-i scrie.

Sintaxă
abstract class Nume(val comun: Tip) {
    abstract val proprietate: Tip
    abstract fun metoda(): Tip       // fără corp
    fun metodaComuna() = ...         // cu corp
}

class Concreta(...) : Nume(...) { override ... }

Ce face abstract

O clasă abstract este o clasă „neterminată”: nu poți crea obiecte din ea (Forma() nu compilează), doar din subclasele ei. Ea poate avea membri abstract, fără corp, pe care fiecare subclasă concretă îi implementează cu override. O clasă abstractă este automat open, la fel și membrii ei abstracți.

Spre deosebire de o interfață, o clasă abstractă poate avea constructor și stare (proprietăți cu valori), iar metodele ei obișnuite pot folosi metodele abstracte (șablonul general este scris o dată, detaliile în subclase). O clasă poate moșteni o singură clasă abstractă.

Exemple

Exemplul 1: Forme geometrice

descriere() este scrisă o singură dată în clasa abstractă și folosește aria(), implementată diferit de fiecare formă.

import kotlin.math.PI

abstract class Forma(val nume: String) {
    abstract fun aria(): Double
    fun descriere() = "$nume cu aria %.2f".format(aria())
}

class Cerc(val raza: Double) : Forma("Cerc") {
    override fun aria() = PI * raza * raza
}

class Dreptunghi(val l: Double, val h: Double) : Forma("Dreptunghi") {
    override fun aria() = l * h
}

fun main() {
    val forme = listOf(Cerc(1.0), Dreptunghi(3.0, 4.5), Cerc(2.5))
    forme.forEach { println(it.descriere()) }
    println("Aria totală: %.2f".format(forme.sumOf { it.aria() }))
}

Rezultat

Cerc cu aria 3.14
Dreptunghi cu aria 13.50
Cerc cu aria 19.63
Aria totală: 36.28

Exemplul 2: Stare comună și proprietate abstractă

Angajat ține numele și salariul de bază; bonus este abstract și diferă pentru fiecare tip de angajat.

abstract class Angajat(val nume: String, protected val salariuBaza: Int) {
    abstract val bonus: Int
    fun salariuTotal() = salariuBaza + bonus
}

class Programator(nume: String) : Angajat(nume, 9000) {
    override val bonus = 1500
}

class Vanzator(nume: String, val vanzari: Int) : Angajat(nume, 5000) {
    override val bonus get() = vanzari / 10
}

fun main() {
    val echipa = listOf(Programator("Andrei"), Vanzator("Maria", 42_000))
    for (a in echipa) println("${a.nume}: ${a.salariuTotal()} lei")
}

Rezultat

Andrei: 10500 lei
Maria: 9200 lei

Greșeli frecvente

  • Forma("x"): eroare cannot create an instance of an abstract class.
  • O subclasă care nu implementează toți membrii abstracți: eroare class 'X' is not abstract and does not implement abstract base class member. Implementează-i sau marchează și subclasa abstract.
  • Alegerea unei clase abstracte când nu ai stare comună: o interfață e mai flexibilă, pentru că o clasă poate implementa mai multe interfețe.

Comenzi înrudite

  • interface: interface descrie ce poate face un obiect (metode și proprietăți), fără stare; o clasă poate implementa mai multe interfețe.
  • open și final: În Kotlin clasele și metodele sunt final implicit; open permite moștenirea sau suprascrierea, iar final o oprește din nou.
  • override și super: override înlocuiește o metodă sau o proprietate moștenită; super apelează versiunea din clasa de bază sau din interfață.
  • sealed class și sealed interface: sealed declară o ierarhie închisă: toate subtipurile sunt cunoscute la compilare, deci when le poate verifica pe toate fără else.
  • private și protected: private ascunde o declarație în clasa sau fișierul ei; protected o face vizibilă și în subclase.