1

在 Kotlin 中,有时我必须使用双重可空性。例如,当我想使用T?whereT可能是可空类型时,我需要双重可空性。有几种方法可以做到这一点:

  1. Holder<T>?Holder在哪里data class Holder<out T>(val element: T)-示例1
  2. boolean标志变量 -示例1
  3. containsKey例如Map<K, T?>-示例1
  4. 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()
}

第一个问题Optionalpublic 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,则测试将通过。

:有什么方法可以在不删除inline修饰符的情况下解决这些问题Optional


1示例包括一些上下文。请在写下我添加了错误链接之前完整阅读它们。

4

1 回答 1

0

我在我的一个项目中实现了完全相同的实用程序:OptionalValue.kt。我的实现与你的非常相似,它也是一个内联/值类,所以它应该是 cpu/memory 高效的,并且它通过了我扔给它的所有测试。

关于您的第一个问题:关于公共构造函数。有一个专门针对这种情况的注释:@PublishedApi。我试图从您的示例中重现 ClassCastException,但它对我来说没有问题,所以我相信这是 Kotlin 本身的一个错误(?)。

另外,为了回答为什么我们需要双重可空性的问题,我在这里解释了我的观点

于 2021-05-21T08:34:54.980 回答