В Котлине иногда приходится работать с двойной обнуляемостью. Например, мне нужна двойная обнуляемость, когда я хочу использовать T?
, где T
может быть обнуляемым типом. Есть несколько подходов для этого:
Holder<T>?
, где Holder
- data class Holder<out T>(val element: T)
- пример 1 boolean
переменная флага - пример 1 containsKey
для Map<K, T?>
- пример 1 - Специальный
UNINITIALIZED_VALUE
для представления второго типа null
- пример 1
Последний подход имеет лучшую производительность,но это также наиболее подвержен ошибкам. Поэтому я решил инкапсулировать его в inline class Optional<T>
:
inline class Optional<out T> @Deprecated(
message = "Not type-safe, use factory method",
replaceWith = ReplaceWith("Optional.of(_value)")
) constructor(private val _value: Any?) {
val value: T?
get() =
@Suppress("UNCHECKED_CAST")
if (isPresent) _value as T
else null
val isPresent: Boolean
get() = _value != NULL
companion object {
@Suppress("DEPRECATION")
fun <T> of(value: T) = Optional<T>(value)
fun <T : Any> ofNullable(value: T?): Optional<T> =
if (value == null) EMPTY
else of(value)
@Suppress("DEPRECATION")
val EMPTY = Optional<Nothing>(NULL)
}
private object NULL
}
inline fun <T> Optional<T>.ifPresent(code: (T) -> Unit) {
@Suppress("UNCHECKED_CAST")
if (isPresent) return code(value as T)
}
inline fun <T> Optional<T>.or(code: () -> T): T {
ifPresent { return it }
return code()
}
Первая проблема с этим Optional
- public constructor
, которая позволяет создавать экземпляры с аргументами несоответствующего типа.
Вторая проблема была замечена во время тестирования. Вот неудачный тест:
emptyOr { Optional.EMPTY }.value assertEql null
fun <T> emptyOr(other: () -> T): T = Optional.EMPTY.or(other)
Исключение:
Exception ClassCastException: Optional$NULL cannot be cast to Optional
at (Optional.kt:42) // emptyOr { Optional.EMPTY }.value assertEql null
Если я удалю модификатор inline
из Optional
, тест пройдет.
Q : Есть ли способ исправить эти проблемы, не удаляя модификатор inline
из Optional
?
1 Примеры включают некоторый контекст. Пожалуйста, прочитайте их полностью, прежде чем писать, что я добавил неправильные ссылки.