companion object în Kotlin

Clase și obiecte Kotlin 1.0

companion object ține membrii care aparțin clasei, nu fiecărui obiect: constante, funcții de creare, contoare comune.

Sintaxă
class Nume {
    companion object {
        const val CONSTANTA = 10
        fun creeaza(...): Nume = Nume(...)
    }
}

Nume.creeaza(...)        Nume.CONSTANTA

Ce face companion object

Kotlin nu are cuvântul static din Java. Membrii care țin de clasă în întregul ei (o constantă, o funcție de creare, un contor de obiecte create) se pun într-un companion object din interiorul clasei. Se folosesc cu numele clasei: Utilizator.MAX_LUNGIME, Utilizator.dinEmail("...").

Un companion este un obiect adevărat: poate avea un nume (companion object Fabrica; implicit se numește Companion), poate implementa interfețe și poate primi funcții de extensie (fun Utilizator.Companion.test()). Codul din companion vede membrii privați ai clasei, inclusiv un constructor private.

O clasă poate avea un singur companion object. Este creat o singură dată, la prima folosire a clasei.

Exemple

Exemplul 1: Funcție de creare și contor comun

Constructorul este privat, deci obiectele se creează doar prin nou, care numără câte au fost create.

class Bilet private constructor(val numar: Int, val spectacol: String) {
    companion object {
        private var vandute = 0
        const val LOCURI = 3

        fun nou(spectacol: String): Bilet? {
            if (vandute == LOCURI) return null
            vandute++
            return Bilet(vandute, spectacol)
        }
    }
}

fun main() {
    repeat(4) {
        val b = Bilet.nou("Hamlet")
        println(if (b == null) "Sală plină!" else "Bilet ${b.numar}/${Bilet.LOCURI} la ${b.spectacol}")
    }
}

Rezultat

Bilet 1/3 la Hamlet
Bilet 2/3 la Hamlet
Bilet 3/3 la Hamlet
Sală plină!

Exemplul 2: Mai multe funcții de creare cu nume clare

În loc de mai mulți constructori, companion-ul oferă funcții cu nume care spun de unde vine valoarea.

class Temperatura private constructor(val celsius: Double) {
    companion object {
        fun dinCelsius(c: Double) = Temperatura(c)
        fun dinFahrenheit(f: Double) = Temperatura((f - 32) * 5 / 9)
    }

    override fun toString() = "%.1f °C".format(celsius)
}

fun main() {
    println(Temperatura.dinCelsius(21.5))
    println(Temperatura.dinFahrenheit(98.6))
}

Rezultat

21.5 °C
37.0 °C

Exemplul 3: Companion cu nume care implementează o interfață

Fabrica implementează Parser, deci clasa Produs însăși poate fi dată unde se cere un Parser<Produs>.

interface Parser<T> { fun parseaza(text: String): T }

data class Produs(val nume: String, val pret: Int) {
    companion object Fabrica : Parser<Produs> {
        override fun parseaza(text: String): Produs {
            val (nume, pret) = text.split(";")
            return Produs(nume, pret.toInt())
        }
    }
}

fun <T> citesteToate(linii: List<String>, parser: Parser<T>) = linii.map { parser.parseaza(it) }

fun main() {
    println(Produs.parseaza("Pâine;5"))
    println(citesteToate(listOf("Lapte;8", "Ouă;12"), Produs))
    println(Produs.Fabrica === Produs)
}

Rezultat

Produs(nume=Pâine, pret=5)
[Produs(nume=Lapte, pret=8), Produs(nume=Ouă, pret=12)]
true

Greșeli frecvente

  • Căutarea lui static: nu există în Kotlin. Folosește companion object sau funcții la nivelul de sus al fișierului.
  • Două blocuri companion object în aceeași clasă: nu este permis.
  • Accesarea proprietăților unui obiect din companion: companion-ul nu are un this al obiectului (nu știe la care exemplar să se uite); primește obiectul ca parametru.

Comenzi înrudite

  • object: object declară o clasă cu un singur obiect (singleton) sau creează pe loc un obiect anonim care implementează o interfață.
  • const val: const val declară o constantă cunoscută la compilare (număr, text, Boolean), înlocuită direct în cod.
  • constructor și init: constructor declară un constructor secundar (sau unul primar cu modificatori); blocurile init rulează la crearea fiecărui obiect.
  • class: class declară o clasă: un tip nou care grupează date (proprietăți) și comportament (metode).