private și protected în Kotlin

private ascunde o declarație în clasa sau fișierul ei; protected o face vizibilă și în subclase.

Sintaxă
class A {
    private val x = 1          // doar în clasa A
    protected val y = 2        // în A și în subclasele ei
}

private fun ajutor() { }       // la nivelul de sus: doar în acest fișier

Ce face private și protected

private pe un membru al unei clase înseamnă că doar codul din acea clasă îl poate folosi. Este felul obișnuit de a ascunde starea internă (de exemplu soldul unui cont), ca ea să se schimbe doar prin metode care verifică regulile. O declarație private la nivelul de sus al fișierului (funcție, clasă, proprietate) este vizibilă doar în acel fișier.

protected se folosește doar în clase: membrul este vizibil în clasă și în subclasele ei, dar nu și din exterior. Spre deosebire de Java, protected în Kotlin nu dă acces celorlalte clase din același pachet.

Un membru override păstrează vizibilitatea din bază dacă nu o schimbi. Poți face privat doar setterul unei proprietăți: var sold = 0; private set.

Exemple

Exemplul 1: private în clasă și în fișier

sold se schimbă doar prin depune/retrage. formateaza este privată în fișier: o folosește doar codul de aici.

private fun formateaza(suma: Int) = "%,d lei".format(java.util.Locale.ROOT, suma).replace(',', '.')

class Cont(val titular: String) {
    private var sold = 0
    private val istoric = mutableListOf<String>()

    fun depune(suma: Int) {
        sold += suma
        istoric.add("+$suma")
    }

    fun retrage(suma: Int) {
        if (suma <= sold) { sold -= suma; istoric.add("-$suma") } else istoric.add("refuzat $suma")
    }

    fun extras() = "$titular: ${formateaza(sold)} (${istoric.joinToString()})"
}

fun main() {
    val cont = Cont("Ana Pop")
    cont.depune(12_500)
    cont.retrage(3_000)
    cont.retrage(50_000)
    println(cont.extras())
    // cont.sold = 1_000_000   // eroare: cannot access 'var sold: Int': it is private in 'Cont'
}

Rezultat

Ana Pop: 9.500 lei (+12500, -3000, refuzat 50000)

Exemplul 2: protected: vizibil în subclase

Notificare pune la dispoziția subclaselor metoda antet, dar codul din main nu o poate apela.

abstract class Notificare(protected val destinatar: String) {
    protected fun antet() = "Către $destinatar"
    abstract fun trimite(): String
}

class Email(adresa: String) : Notificare(adresa) {
    override fun trimite() = "${antet()} | email trimis"
}

class Sms(telefon: String) : Notificare(telefon) {
    override fun trimite() = "${antet()} | SMS trimis la ${destinatar.takeLast(4)}"
}

fun main() {
    val lista = listOf(Email("ana@exemplu.ro"), Sms("0722123456"))
    lista.forEach { println(it.trimite()) }
    // lista[0].antet()   // eroare: cannot access 'fun antet(): String': it is protected in 'Notificare'
}

Rezultat

Către ana@exemplu.ro | email trimis
Către 0722123456 | SMS trimis la 3456

Exemplul 3: Mesajul compilatorului pentru un membru privat

Codul din afara clasei încearcă să scrie direct în sold. Compilarea se oprește cu eroarea de mai jos (exact asta s-ar întâmpla și dacă ai scoate comentariul din primul exemplu).

class Cont(val titular: String) {
    private var sold = 0
    fun depune(suma: Int) { sold += suma }
}

fun main() {
    val cont = Cont("Ana Pop")
    cont.depune(100)
    cont.sold = 1_000_000
}

Rezultat la compilare

main.kt:9:10: error: cannot access 'var sold: Int': it is private in 'Cont'.
    cont.sold = 1_000_000
         ^^^^

Greșeli frecvente

  • Așteptarea ca protected să dea acces în tot pachetul, ca în Java: în Kotlin dă acces doar subclaselor.
  • protected la nivelul de sus al fișierului: nu este permis (nu există o clasă de moștenit).
  • Ascunderea unei liste private val dar returnarea ei directă (fun lista() = istoric): codul din afară primește aceeași MutableList și o poate modifica. Returnează istoric.toList().

Comenzi înrudite

  • public și internal: public (implicit) face o declarație vizibilă oriunde; internal o face vizibilă doar în modulul în care este scrisă.
  • class: class declară o clasă: un tip nou care grupează date (proprietăți) și comportament (metode).
  • get și set (getteri și setteri): get și set definesc ce se întâmplă când o proprietate este citită sau modificată; field este valoarea păstrată în spate.
  • abstract: abstract marchează o clasă care nu poate fi creată direct și membri fără implementare, pe care subclasele trebuie să-i scrie.
  • open și final: În Kotlin clasele și metodele sunt final implicit; open permite moștenirea sau suprascrierea, iar final o oprește din nou.