W pierwszych wersjach Kotlin do ograniczenia hierarchii typów używano klas sealed, które pozwalały programistom wyraźnie kontrolować dozwolone podtypy poprzez ograniczenie deklaracji dziedziczy jedynie w jednym pliku. W niektórych zadaniach to nie wystarczało, zwłaszcza gdy trzeba było opisać podobne hierarchie za pomocą interfejsów. W celu rozwiązania tego problemu, od wersji Kotlin 1.5, wprowadzono modyfikator sealed dla interfejsów.
Zwykłe interfejsy pozwalają na ich implementację w dowolnym miejscu projektu, co często przeszkadza w zapewnieniu absolutnej zamkniętości hierarchii typów i wykorzystaniu wyczerpujących sprawdzeń typów (exhaustive when). Może to prowadzić do błędów w czasie działania, uniemożliwiając statyczne sprawdzenie wszystkich wariantów.
Interfejsy sealed pozwalają na ograniczenie listy klas-implementacji w ramach jednego pliku, zwiększając przewidywalność systemu typów i bezpieczeństwo dopasowywania wzorców (pattern matching).
sealed interface NetworkResult class Success(val data: String): NetworkResult class Failure(val error: Throwable): NetworkResult fun handle(result: NetworkResult) = when(result) { is Success -> println("Dane: ${result.data}") is Failure -> println("Błąd: ${result.error}") // Wszystkie warianty uwzględnione, 'else' niepotrzebne }
Kluczowe cechy:
Czy można dodać interfejs sealed do osobnego pliku lub zaimplementować go poza pierwotnym plikiem?
Nie. Wszystkie typy bezpośrednio implementujące interfejs sealed muszą być zadeklarowane w tym samym pliku, w przeciwnym razie kompilator zgłosi błąd.
Czy interfejs sealed może mieć podinterfejsy poza plikiem deklaracji?
Nie. Podinterfejsy także muszą znajdować się w tym samym pliku. Cała hierarchia jest zamknięta w ramach pliku.
Czy interfejsy sealed wpływają na wydajność w czasie działania?
Nie bezpośrednio, ale pozwalają stosować bezpieczne sprawdzenia typów na etapie kompilacji, co zmniejsza prawdopodobieństwo błędów w czasie działania, upraszcza kod i może pośrednio przyspieszyć testowanie.
Programista deklaruje interfejs sealed NetworkAction w jednym pliku, ale implementacje są rozproszone w różnych klasach i plikach. Problem ujawnia się późno: kompilator sygnalizuje naruszenie reguły, naprawienie struktury jest trudne, zwłaszcza w dużym projekcie.
Zalety:
Wady:
Interfejs sealed OrderResult oraz klasy Success/Failure są zadeklarowane w jednym pliku. Sprawdzenie when jest zawsze wyczerpujące, żaden przypadek nie jest pominięty.
Zalety:
Wady: