Comments 8
Да, Lombok
это часто удобно, но реализовать UIState
удобнее на рекордах:
public sealed interface UIState permits UIState.Loading, UIState.Error, UIState.Idle, UIState.Success {
record Loading(boolean isLoading) implements UIState {}
record Error(String message) implements UIState {}
record Success(Object data) implements UIState {}
enum Idle implements UIState {INSTANCE}
}
и вот пример работы с ним:
public void processUIState(UIState uiState) {
switch (uiState) {
case UIState.Loading loading -> System.out.println("loading: " + loading.isLoading());
case UIState.Error error -> System.out.println("error: " + error.message());
case UIState.Success success -> System.out.println("success: " + success.data());
case UIState.Idle __ -> System.out.println("idle");
default -> throw new IllegalStateException("Unexpected value: " + uiState);
}
}
Даже с типизированным Maybe
фактически нет проблем, причём за счёт использования enum
создать ещё один инстанс Nothing
даже через рефлексию будет проблематично.
public sealed interface Maybe<T> permits Maybe.Just, Maybe.Nothing {
@Nonnull
@SuppressWarnings("unchecked")
static <T> Maybe<T> maybe(@Nullable T value) {
return value == null ? (Maybe<T>) Nothing.INSTANCE : new Just<>(value);
}
record Just<T>(T value) implements Maybe<T> {}
enum Nothing implements Maybe<Object> {INSTANCE}
}
Интересно что структура sealed
классов не обязана быть полностью закрытой так как один из разрешённых вариантов может иметь модификатор non-sealed
и от него можно будет наследоваться.
Может быть полезным для библиотек и прочих мест где нужно всё же дать возможность неконтролируемо расширять иерархию классов.
public sealed interface Demo permits Demo.Sealed1, Demo.Sealed2, Demo.Open {
// Эти классы входят в закрытую часть структуры
final class Sealed1 implements Demo {}
final class Sealed2 implements Demo {}
non-sealed class Open implements Demo {}
// Это пример структуры, которая уже не контролируется ограничениями по `sealed`
class External extends Open {}
}
Этотвсе прекрасно конечно, но блин, тут же явно получается циклическая зависимость между предком и наследником. Вот это точно повод не использовать эти классы.
Я так понимаю что наличие информации о всех наследниках в родителе это не побочная проблема, а одна из целей функционала.
А ещё такая зависимость есть в enum
-ах, причём даже более жёсткая: родительский класс не только знает все его реализации, но они ещё и объявлены должны быть строго в пределах родительского класса.
Тогда как наследники sealed
-класса могут быть объявлены где угодно (но в пределах видимости родительского - так как в нём необходимо указать потомка). Ну и с учётом возможности дать возможность расширять иерархию классов через non-sealed
потомка это вообще не выглядит не преодолимой проблемой.
Другое дело что не все классы имеет смысл объявлять sealed
.
Может пропустил, но не сказали о главной особенности. Если приложение не использует java модули то все наследники должны быть в одном пакете.
Глубокое погружение в Sealed классы и интерфейсы