inner class în Kotlin

Clase și obiecte Kotlin 1.0

inner marchează o clasă din interiorul alteia care ține o referință la obiectul exterior și îi poate folosi membrii.

Sintaxă
class Exterioara {
    class Imbricata { ... }           // nu vede obiectul exterior
    inner class Interioara { ... }    // vede membrii exteriorului (this@Exterioara)
}

Exterioara.Imbricata()
exterior.Interioara()

Ce face inner class

O clasă scrisă în interiorul altei clase este, implicit, doar imbricată (nested): numele ei este grupat sub clasa exterioară (Magazin.Adresa), dar nu are acces la un obiect anume al clasei exterioare. Se creează cu Magazin.Adresa(...). Este echivalentul lui static class din Java.

Cu inner class, fiecare obiect interior aparține unui obiect exterior și îi poate folosi proprietățile și metodele (inclusiv cele private). Se creează pornind de la un obiect exterior: magazin.Raion("Lactate"). Pentru a ajunge explicit la obiectul exterior scrii this@Magazin.

Un obiect inner ține obiectul exterior în memorie cât timp trăiește el. Folosește inner doar când ai nevoie de acces la exterior.

Exemple

Exemplul 1: Clasă imbricată și clasă inner

Adresa nu vede nume. Raion este inner, deci folosește direct nume al magazinului din care a fost creat.

class Magazin(val nume: String) {
    class Adresa(val oras: String, val strada: String) {
        fun text() = "$strada, $oras"
    }

    inner class Raion(val denumire: String) {
        fun eticheta() = "$nume - raionul $denumire"
    }
}

fun main() {
    val adresa = Magazin.Adresa("Iași", "Str. Păcurari 5")
    println(adresa.text())

    val mega = Magazin("Mega Iași")
    val lactate = mega.Raion("Lactate")
    println(lactate.eticheta())
}

Rezultat

Str. Păcurari 5, Iași
Mega Iași - raionul Lactate

Exemplul 2: Clasa inner modifică starea exteriorului

Fiecare Rand adaugă la totalul facturii din care face parte. this@Factura arată clar că totalul este al facturii.

class Factura(val numar: String) {
    private var total = 0.0
    private val randuri = mutableListOf<Rand>()

    inner class Rand(val produs: String, val cantitate: Int, val pret: Double) {
        init {
            this@Factura.total += cantitate * pret
            randuri.add(this)
        }
    }

    fun tipareste() {
        println("Factura $numar")
        for (r in randuri) println("  ${r.produs} x${r.cantitate} = ${r.cantitate * r.pret}")
        println("  Total: $total lei")
    }
}

fun main() {
    val f = Factura("F-2026-17")
    f.Rand("Caiet", 3, 4.5)
    f.Rand("Pix", 2, 2.0)
    f.tipareste()
}

Rezultat

Factura F-2026-17
  Caiet x3 = 13.5
  Pix x2 = 4.0
  Total: 17.5 lei

Greșeli frecvente

  • Folosirea membrilor clasei exterioare dintr-o clasă imbricată fără inner: eroare outer class 'class Magazin' of non-inner class cannot be used as receiver.
  • Crearea unei clase inner fără obiect exterior: Magazin.Raion("x") nu compilează; trebuie magazin.Raion("x").
  • Marcarea cu inner din obișnuință: obiectele inner țin în viață obiectul exterior. Dacă nu ai nevoie de acces, las-o imbricată.

Comenzi înrudite

  • class: class declară o clasă: un tip nou care grupează date (proprietăți) și comportament (metode).
  • this: this înseamnă obiectul curent: obiectul pe care s-a apelat metoda sau receptorul unei funcții de extensie.
  • object: object declară o clasă cu un singur obiect (singleton) sau creează pe loc un obiect anonim care implementează o interfață.
  • private și protected: private ascunde o declarație în clasa sau fișierul ei; protected o face vizibilă și în subclase.