Sobes.tech
Middle

Jaka jest różnica między klasami sealed a klasami abstrakcyjnymi w Kotlin?

sobes.tech AI

Odpowiedź od AI

Główna różnica między klasami sealed a abstrakcyjnymi polega na tym, że klasy sealed ograniczają hierarchię dziedziczenia do jednego pliku lub modułu (dla Kotlin 1.5+). Następcy klasy sealed muszą być zadeklarowani w tym samym pliku co sama klasa sealed, co zapewnia wyczerpującą kontrolę podczas korzystania z wyrażeń when.

Różnice:

  • Dziedziczenie:
    • Klasy abstrakcyjne mogą być dziedziczone w dowolnym miejscu projektu.
    • Klasy sealed mogą być dziedziczone tylko w tym samym pliku (przed Kotlin 1.5) lub w tym samym module (z Kotlin 1.5+).
  • Użycie z when:
    • Używając when z typem sealed, kompilator może sprawdzić, czy wszystkie możliwe podtypy zostały uwzględnione, co czyni when wyczerpującym i eliminuje konieczność jawnego podawania gałęzi else, jeśli wszystkie podtypy są obsłużone.
    • Z klasami abstrakcyjnymi kompilator nie może zagwarantować wyczerpywalności, dlatego często konieczne jest dodanie else.
  • Tworzenie instancji:
    • Ani klasy abstrakcyjne, ani sealed nie mogą być bezpośrednio instancjonowane.
  • Członkowie:
    • Zarówno klasy abstrakcyjne, jak i sealed mogą mieć członków abstrakcyjnych i nieabstrakcyjnych.

Przykład klasy sealed:

sealed class Result<out T> {
    data class Success<out T>(val data: T) : Result<T>()
    data class Error(val message: String) : Result<Nothing>()
}

fun processResult(result: Result<Int>) {
    when (result) {
        is Result.Success -> {
            // Obsługa sukcesu
            println("Sukces: ${result.data}")
        }
        is Result.Error -> {
            // Obsługa błędu
            println("Błąd: ${result.message}")
        }
        // Nie jest potrzebny else, jeśli obsłużono wszystkie podtypy
    }
}

Przykład klasy abstrakcyjnej:

abstract class Shape {
    abstract fun area(): Double
}

class Circle(val radius: Double) : Shape() {
    override fun area(): Double = Math.PI * radius * radius
}

class Rectangle(val width: Double, val height: Double) : Shape() {
    override fun area(): Double = width * height
}

fun calculateArea(shape: Shape): Double {
    return shape.area()
}

Kiedy używać:

  • Klasy sealed: Doskonałe do reprezentowania ograniczonego zestawu stanów lub typów, gdy potrzebujesz wyczerpującej kontroli w wyrażeniach when. Często używane do modelowania wyników operacji (sukces/błąd), stanów UI, zdarzeń.
  • Klasy abstrakcyjne: Odpowiednie do definiowania ogólnej funkcjonalności i kontraktów dla grupy powiązanych klas, które mogą być dziedziczone w dowolnym miejscu w projekcie.

Podsumowując, klasy sealed zapewniają bardziej rygorystyczną kontrolę nad hierarchią dziedziczenia i poprawiają bezpieczeństwo oraz czytelność kodu podczas obsługi ograniczonego zestawu podtypów.