5.8Kпросмотров
29 сентября 2024 г.
Score: 6.4K
Hello nullability my old friend Котлин давно вошёл в грешную жизнь Android SDK, и с годами в Java-коде проросли аннотации нуллабельности. Compose стал повсеместно использовать value-классы — а компилятор доэволюционировал и перестал рассыпаться от одного их вида. Но пришла новая напасть: значения вроде {Dp, Color, Size, Offset, …}.Unspecified. Теперь можно запросить «обводку непонятного цвета и неизвестной толщины», компилятор этому не препятствует. В парадигме, где все параметры опциональные, а фоллбэки можно забрать из CompositionLocal, это удобно. Хотя идея, что у Text("LOL") «откуда-то» берётся цвет, размер, шрифт и начертание, совершенно нездоровая. А вот при вызове Dp.toPx() всё рушится, ведь, как учили в первом классе, NaN умноженный на число даёт NaN. Почему же нельзя использовать типы Color? и Dp??
Потому что они будут бокситься, а композ станет тормозить ещё более чудовищно. Заявить, что в терминах данного value-класса null выражается через Float.NaN, также нельзя — null хардкодом прописан в компилятор. Нельзя и сделать sealed-иерархию value-классов вида
sealed value class OptionDp(…) { object Unspecified : OptionDp(Float.NaN) class Dp(…) : OptionDp(…)
}
KEEP на это есть, но он заброшен. Там предлагают различать варианты силеда посредством боксинга, оставив только один вариант труъ-инлайн, так что данное направление бесперспективно. Возможен ещё один вариант: симулировать иерархию классов, не уповая на сабтайпинг в языке.
value class OptionDp(…) { /companion/ val Unspecified = OptionDp(Float.NaN) fun unwrap() = Dp(value)
}
value class Dp(…) { init { require(value.isFinite()) } val option get() = OptionDp(value)
}
Не хотелось бы везде писать 32.dp.option, да и замена Dp на OptionDp становится ломающим изменением. Можно добавить новый уровень сложности: интерфейс AsOptionDp с единственным методом, который реализовывали бы и Dp, и OptionDp. И пусть библиотечная функция вызывает метод конвертации. Добро пожаловать в рубрику «к Мишиному сожалению, Котлин не Раст»: даже при передаче в inline-функцию value-класс боксится, а вызов остаётся виртуальным. Итого: не нужно допускать новых null-значений. Явно передать лишний параметр не страшно. Страшно рисовать «обводку непонятного цвета и неизвестной толщины».