Ce face constructor și init
Constructorul primar se scrie în antetul clasei: class Elev(val nume: String). El nu are corp, așa că codul care trebuie să ruleze la crearea obiectului (validări, calcule) se pune în blocuri init { }. Blocurile init și inițializările proprietăților rulează în ordinea în care apar în clasă.
Un constructor secundar se declară în corpul clasei cu constructor(...). Dacă clasa are constructor primar, cel secundar trebuie să-l apeleze cu : this(...). Corpul constructorului secundar rulează după toate blocurile init. În Kotlin ai nevoie rar de constructori secundari: valorile implicite ale parametrilor rezolvă de obicei aceeași problemă.
Cuvântul constructor se scrie și în antet când constructorul primar are un modificator sau o adnotare: class Conexiune private constructor(...). Un constructor privat obligă codul din afară să folosească o funcție de creare (de obicei în companion object).
Exemple
Exemplul 1: Ordinea: proprietăți și blocuri init
Mesajele arată ordinea reală: inițializările și blocurile init rulează de sus în jos, la fiecare obiect creat.
class Elev(val nume: String, nota: Int) {
val notaCorectata = nota.coerceIn(1, 10).also { println("1. calculez nota pentru $nume") }
init {
println("2. primul init: $nume are nota $notaCorectata")
}
val promovat = (notaCorectata >= 5).also { println("3. calculez dacă $nume a promovat") }
init {
println("4. al doilea init: promovat = $promovat")
}
}
fun main() {
Elev("Ana", 12)
println("---")
Elev("Radu", 4)
}
Rezultat
1. calculez nota pentru Ana
2. primul init: Ana are nota 10
3. calculez dacă Ana a promovat
4. al doilea init: promovat = true
---
1. calculez nota pentru Radu
2. primul init: Radu are nota 4
3. calculez dacă Radu a promovat
4. al doilea init: promovat = false
Exemplul 2: Validare în init
require din init oprește crearea unui obiect cu date greșite.
class Temperatura(val grade: Double) {
init {
require(grade >= -273.15) { "Sub zero absolut: $grade" }
}
}
fun main() {
println(Temperatura(21.5).grade)
try {
Temperatura(-300.0)
} catch (e: IllegalArgumentException) {
println("Eroare: ${e.message}")
}
}
Rezultat
21.5
Eroare: Sub zero absolut: -300.0
Exemplul 3: Constructor secundar și constructor privat
Constructorul secundar primește un text de forma „Ana, 21” și apelează constructorul primar cu this(...). Factura are constructorul privat, deci se creează doar prin Factura.noua.
class Persoana(val nume: String, val varsta: Int) {
constructor(text: String) : this(text.substringBefore(",").trim(), text.substringAfter(",").trim().toInt()) {
println("Creată din text: \"$text\"")
}
}
class Factura private constructor(val numar: String) {
companion object {
private var urmatorul = 1
fun noua() = Factura("F-%04d".format(urmatorul++))
}
}
fun main() {
val p = Persoana("Ana Pop, 21")
println("${p.nume} are ${p.varsta} ani")
println(Persoana("Mihai", 30).nume)
println(Factura.noua().numar)
println(Factura.noua().numar)
}
Rezultat
Creată din text: "Ana Pop, 21"
Ana Pop are 21 ani
Mihai
F-0001
F-0002
Greșeli frecvente
- Constructor secundar care nu apelează constructorul primar: eroare
primary constructor call expected. Adaugă: this(...). - Folosirea unei proprietăți într-un
initscris înaintea ei: proprietatea nu este încă inițializată (eroarevariable 'b' must be initialized). - Mulți constructori secundari pentru variante de apel: de obicei e mai simplu cu valori implicite:
class Elev(val nume: String, val clasa: String = "a IX-a").
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.companion object: companion object ține membrii care aparțin clasei, nu fiecărui obiect: constante, funcții de creare, contoare comune.require: Verifică argumentele unei funcții și aruncă IllegalArgumentException dacă nu sunt bune; requireNotNull cere o valoare diferită de null.lateinit: lateinit permite unei proprietăți var ne-nullable să primească valoare mai târziu, după crearea obiectului.