ПрограммированиеAndroid/Kotlin-разработчик

Что такое sealed интерфейсы в Kotlin, как и зачем их использовать?

Проходите собеседования с ИИ помощником Hintsage

Ответ.

Sealed interface — это специальный тип интерфейса в Kotlin, позволяющий ограничить множество его реализаций в рамках одного модуля. Впервые sealed классы появились в Kotlin раньше, а sealed интерфейсы были добавлены начиная с Kotlin 1.5 как эволюция для большего контроля над типами, участвующими, например, в иерархиях состояний или обработке событий.

История вопроса

Ранее разработчики применяли sealed классы для ограничения наследования и создания безопасных иерархий. Однако для гибкости и поддержки структур где наследование от нескольких типов полезно, понадобились sealed интерфейсы.

Проблема

Без sealed интерфейсов нельзя гибко управлять набором подклассов интерфейса. Это делает невозможным, например, исчерпывающую проверку when при обработке состояний, если всё построено на интерфейсах, а не только на абстрактных/конкретных классах.

Решение

Использование sealed interface позволяет:

  • Описать фиксированное множество реализаций.
  • Гарантировать, что все реализации известны компилятору.
  • Безопасно использовать when без ветки else — компилятор подскажет о неохваченных кейсах.

Пример кода:

sealed interface Event class Click : Event class Scroll : Event fun handle(event: Event) = when(event) { is Click -> println("Click event") is Scroll -> println("Scroll event") }

Ключевые особенности:

  • Сужение и контроль набора допустимых реализаций.
  • Возможность наследования от нескольких sealed интерфейсов одновременно.
  • Безопасность при pattern matching (when).

Вопросы с подвохом.

Могут ли sealed интерфейсы иметь реализации вне файла их объявления?

Нет, реализации sealed интерфейса должны находиться в том же модуле. Это обеспечивает полную исчерпаемость и позволяет компилятору контролировать их количество.

Как sealed интерфейсы взаимодействуют с классами и объектами?

Sealed интерфейс может реализовываться как обычными, так и object-классами, а также data object (Kotlin 1.9+). Такой интерфейс может присутствовать в множественном наследовании, чего нельзя сделать с sealed классом.

sealed interface Operation object Add: Operation object Subtract: Operation

Могут sealed интерфейсы быть вложенными?

Да, можно объявлять sealed интерфейс как внутри другого sealed класса, так и поверх других интерфейсов. Главное — все реализации внутри одного модуля.

Типовые ошибки и анти-паттерны

  • Определение реализаций sealed интерфейса в разных модулях приводит к ошибкам компиляции.
  • Попытка использовать sealed интерфейс исходя из предположения что он как sealed класс, хотя sealed интерфейс может наследоваться другими интерфейсами.

Пример из жизни

Негативный кейс

В приложении состояния UI описали просто как интерфейсы без sealed-модификатора. Забыли одну реализацию, статический анализ this не поймал; ошибка всплыла только на проде.

Плюсы:

  • Привычный паттерн Java-интерфейсов.

Минусы:

  • Нет гарантий исчерпываемости в when.
  • Уязвимость к появлению реализаций "извне".

Позитивный кейс

Использование sealed interface для модели событий экрана. Все реализации находятся внутри одного файла модуля, компилятор уведомляет при незакрытых ветках когда в when упущен кейс.

Плюсы:

  • Полная type-safety.
  • Удобство поддержки и расширения.

Минусы:

  • Использование только в рамках одного модуля.