programowanieProgramista Kotlin/Java

Jak działa dziedziczenie konstruktorów w Kotlinie, jak działa wywołanie konstruktorów super, i jakie pułapki istnieją przy łączeniu konstruktorów podstawowych i dodatkowych? Podaj przykłady różnych przypadków, wyjaśnij różnicę z Javą.

Zdaj rozmowy kwalifikacyjne z asystentem AI Hintsage

Odpowiedź

W Kotlinie klasa może mieć jeden konstruktor podstawowy i dowolną liczbę konstruktorów dodatkowych. Różnica w stosunku do Javy — w Kotlinie konstruktor podstawowy jest deklarowany w nagłówku klasy i może zawierać parametry oraz modyfikatory. Konstruatory dodatkowe zawsze muszą wywoływać albo inny konstruktor dodatkowy, albo konstruktor podstawowy, albo konstruktor superklasy (za pomocą słowa kluczowego super).

Kluczowe cechy:

  • Jeśli superklasa ma konstruktor podstawowy z obowiązkowymi parametrami, klasa pochodna zawsze musi jawnie go wywołać poprzez : super(...).
  • W konstruktorze dodatkowym klasa bazowa jest wywoływana przez super(...) lub inny konstruktor dodatkowy/podstawowy.
  • Nie jest dozwolone konfliktowe inicjalizowanie: wszystkie pola muszą być zawsze poprawnie zainicjalizowane.

Przykład użycia z dziedziczeniem:

open class A(val a: Int) { constructor(a: Int, str: String) : this(a) { println("Secondary in A: $str") } } class B : A { constructor(a: Int) : super(a) { println("Secondary in B") } constructor(a: Int, s: String) : super(a, s) { println("Secondary #2 in B") } }

Czym różni się podejście od Javy:

  • W Kotlinie nie można NIE wywołać konstruktora superklasy.
  • Konstruktor podstawowy zawsze inicjalizuje podstawowe parametry i może być niejawny.

Pytanie z pułapką

Czy w Kotlinie można stworzyć klasę pochodną, NIE wywołując jawnie konstruktora superklasy, jeśli w klasie bazowej jest tylko określony konstruktor?

Odpowiedź: Nie, w przeciwieństwie do Javy, w Kotlinie wywołanie konstruktora superklasy jest obowiązkowe i jawnie określane albo w "głównej" części klasy, albo po słowie kluczowym : super().

Przykład błędu:

open class Base(val x: Int) class Derived : Base // Błąd kompilacji!

Przykłady rzeczywistych błędów z powodu nieznajomości szczegółów tematu


Historia

W projekcie mikrousługowym po migracji na Kotlin zapomniano o wyraźnym wskazaniu konstruktora rodzica: konstruktor superklasy z obowiązkowym parametrem nie był wywoływany, serwis nie kompilował się, konieczne było refaktoryzowanie sygnatur.


Historia

Projekt Android miał głęboką hierarchię (Activity → BaseActivity → CustomActivity), dodanie dodatkowego konstruktora traciło parametry, w rezultacie wywoływano niewłaściwy konstruktor bazowy, a część pól pozostawała null — aplikacja padała w czasie wykonywania z NPE.


Historia

W otwartym kodzie biblioteki dodatkowy konstruktor w klasie pochodnej przypadkowo ominął konstruktor podstawowy, co doprowadziło do dwóch różnych gałęzi inicjalizacji: czasami pole było inicjalizowane, czasami — nie. Błąd został odnaleziony dopiero po dłuższym czasie dzięki złożonym raportom o błędach.