programowanieProgramista Kotlin

Jak działają obiekty danych (data object) w Kotlin, do czego są potrzebne, jak realizowane są equals/hashCode/toString i czym różnią się od zwykłych obiektów i klas danych?

Zdaj rozmowy kwalifikacyjne z asystentem AI Hintsage

Odpowiedź.

Historia pytania:

Do Kotlin 1.9 obiekty typu object nie mogły być danymi — nie można było mieć singletonu, który automatycznie uzyskuje equals, hashCode, toString, jak klasy danych. Pojawienie się data object zniosło to ograniczenie. Teraz można utworzyć obiekt singleton z automatycznie generowanymi metodami, odpowiedni do wzorców wartości i podobnych do enum.

Problem:

Wcześniej, aby uzyskać poprawne equals(), hashCode(), toString() nawet dla obiektów singleton, trzeba było je ręcznie implementować lub używać innych sztuczek, co zwiększało BOILERPLATE i możliwość błędów.

Rozwiązanie:

Użyj data object dla obiektów, których instancja jest unikalna, a jednocześnie potrzebne jest standardowe zachowanie equals/hashCode/toString do przekazywania w kolekcjach, serializacji, porównywania i debugowania.

Przykład kodu:

data object NotAvailable fun checkStatus(status: Any) = when (status) { NotAvailable -> "Dane są niedostępne" else -> "Inny status" } val set = setOf(NotAvailable) println(NotAvailable in set) // true println(NotAvailable.toString()) // NotAvailable

Kluczowe cechy:

  • Data object realizuje equals/hashCode/toString zgodnie z kontraktem klasy danych
  • To obiekt singleton (jedyna instancja)
  • Odpowiedni do wzorców podobnych do wartości lub enum bez danych

Pytania z przymrużeniem oka.

Czy data object może zawierać właściwości?

Może, ale tylko właściwości val bez backing field (ponieważ singleton nie powinien przechowywać żadnych danych)

data object Loading { val status: String get() = "Ładowanie..." }

Czym różni się data object od zwykłego obiektu pod względem equals?

Equals w zwykłym obiekcie sprawdza tylko tożsamość referencji, w data object porównywane jest zgodnie z kontraktem danych, ale w przypadku singletonu to zawsze ten sam obiekt. Jednak nadpisane equals/hashCode są bardziej przydatne dla kolekcji.

Czy można dziedziczyć po data object?

Nie, data object jest finalny, tak jak każdy obiekt w Kotlinie — nie można dziedziczyć.

Typowe błędy i antywzorce

  • Używać data object zamiast enum, gdy obiektów jest kilka
  • Używać data object, próbując przechowywać dane — nie do przyjęcia
  • Nie uwzględniać, że równość zawsze jest referencyjna, ale przewidziana przez kolekcje w oparciu o wartość

Przykład z życia

Negatywny przypadek

Zamiast enum dla wszystkich stanów używano różnych data object. Po roku potrzebna była serializacja po nazwach, zaczęto ręcznie porównywać nazwy obiektów z typami.

Zalety:

  • Łatwa inicjalizacja

Wady:

  • Zbędne sztuczki do serializacji, błąd w porównywaniu

Pozytywny przypadek

Dla wartości zwracanej z zapytania sieciowego używano data object dla specjalnych statusów: Loading, Empty, Error. Kod był zwięzły, wsparcie dla equals, hashCode, toString automatycznie.

Zalety:

  • Wygodne do sprawdzania w kolekcjach
  • Ładna logowanie

Wady:

  • Nie można dodać zmiennych właściwości, tylko val-lazy