Historia pytania:
Interfejsy z modyfikatorem sealed pojawiły się w Kotlinie jako rozwinięcie koncepcji sealed class. Do wersji Kotlin 1.5 tylko klasy sealed pozwalały ograniczyć zestaw możliwych dziedziczących, co jest szczególnie ważne dla bezpiecznej pracy z hierarchiami stanów (maszyny stanowe, DSL, itp). Wprowadzenie sealed interfejsów umożliwiło analogiczne ograniczenie implementacji interfejsów, bez przywiązywania się do klas.
Problem:
Prosty, otwarty interfejs może być implementowany wszędzie w programie, co może prowadzić do niekontrolowanego wzrostu liczby implementacji i utrudniać wsparcie kodu. Podczas przetwarzania za pomocą wyrażenia when, kompilator nie może ostrzec o nieobsługiwanych gałęziach.
Rozwiązanie:
Sealed interface ogranicza implementacje tylko do tych, które są zdefiniowane w jednym module (lub tym samym pliku, jeśli interfejs nie jest na najwyższym poziomie). Ten rodzaj kontroli stosuje się do bezpiecznych struktur podobnych do enumów, wzorca ADT i obsługujących stany. Kompilator zna wszystkie implementacje i pomaga w analizie kodu.
Przykład kodu:
sealed interface NetworkResult class Success(val data: String): NetworkResult class Error(val cause: Throwable): NetworkResult object Loading: NetworkResult fun handleResult(result: NetworkResult): String = when (result) { is Success -> "Sukces z ${result.data}" is Error -> "Błąd: ${result.cause.message}" Loading -> "Ładowanie..." }
Kluczowe cechy:
Czy można zaimplementować sealed interface poza bieżącym plikiem?
Odpowiedź: W przeciwieństwie do sealed class, sealed interfejsy mogą być implementowane w innych plikach, ale tylko w ramach bieżącego modułu (lub jednostki kompilacji, jeśli interfejs nie jest na najwyższym poziomie).
Czy sealed interface mogą mieć otwarte metody z implementacją domyślną?
Tak, jak zwykłe interfejsy, sealed interfejs może zawierać domyślne implementacje funkcji.
sealed interface Mode { fun description(): String = "Nieznany tryb" }
Czy możliwe jest serializowanie sealed interface za pomocą standardowych serializatorów (np. kotlinx.serialization)?
Tak, ale będzie konieczne jawne wskazanie wszystkich implementacji. W Kotlinx.serialization wsparcie dla sealed interfejsów nie pojawiło się od razu, ważne jest, aby jawnie wskazać serializowane typy.
W projekcie opisano sealed interfejs dla wszystkich typów stanów UI, a implementacje zaczęły pojawiać się w różnych częściach aplikacji. Potem dodano nowy typ stanu, zapomniano zaktualizować blok obsługi, co doprowadziło do ignorowania nowego stanu w logach.
Zalety:
Wady:
Używaliśmy sealed interfejsu do wszystkich odpowiedzi sieciowych. Dzięki temu, przy dodawaniu nowego typu odpowiedzi, IDE od razu podświetliło wszystkie miejsca, gdzie obsługa przechodzi przez when. Błąd jest poprawiany od razu, nie ma niespodziewanych luk w logice.
Zalety:
Wady: