【发布时间】:2019-10-06 11:15:36
【问题描述】:
在 Kotlin 中,有时我必须使用双重可空性。例如,当我想使用T? 时,我需要双重可空性,其中T 可能是可空类型。有几种方法可以做到这一点:
-
Holder<T>?其中Holder是data class Holder<out T>(val element: T)- example1 -
boolean标志变量 - example1 -
containsKeyforMap<K, T?>- example1 - 代表第二种
null的特殊UNINITIALIZED_VALUE- example1
最后一种方法性能最好,但也最容易出错。所以我决定把它封装在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
如果我从Optional 中删除inline 修饰符,则测试将通过。
问:有什么办法可以在不从Optional 中删除inline 修饰符的情况下解决这些问题?
1 示例包括一些上下文。请在写我添加错误链接之前完整阅读它们。
【问题讨论】:
-
为什么首先要双重可空性?
-
当然,但我认为有更好的方法来设计这些东西,而不是对所有东西都使用空值。
UNINITIALIZED_VALUE的最后一个例子就是一个很好的例子。使用内部对象使意图更加清晰。使用containsKey也比先检查某个持有者是否为空,然后检查值是否为空要清楚得多。 -
无论如何,对于您的问题,在我看来,该异常似乎是内联类的错误。您应该在youtrack.jetbrains.com/issues/KT 上报告问题。
标签: kotlin null nullable kotlin-inline-class