【问题标题】:Is it possible to make safe inline Optional in Kotlin?是否可以在 Kotlin 中使安全内联可选?
【发布时间】:2019-10-06 11:15:36
【问题描述】:

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

  1. Holder<T>? 其中Holderdata class Holder<out T>(val element: T) - example1
  2. boolean 标志变量 - example1
  3. containsKey for Map<K, T?> - example1
  4. 代表第二种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 示例包括一些上下文。请在写我添加错误链接之前完整阅读它们。

【问题讨论】:

  • 为什么首先要双重可空性?
  • @marstran 查看示例 here (Holder&lt;R&gt;?)、here (boolean initialized)、here (containsKey) 和 here (UNINITIALIZED_VALUE)跨度>
  • 当然,但我认为有更好的方法来设计这些东西,而不是对所有东西都使用空值。 UNINITIALIZED_VALUE 的最后一个例子就是一个很好的例子。使用内部对象使意图更加清晰。使用containsKey 也比先检查某个持有者是否为空,然后检查值是否为空要清楚得多。
  • 无论如何,对于您的问题,在我看来,该异常似乎是内联类的错误。您应该在youtrack.jetbrains.com/issues/KT 上报告问题。

标签: kotlin null nullable kotlin-inline-class


【解决方案1】:

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

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

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2012-01-09
    • 2017-12-18
    • 2015-07-30
    • 1970-01-01
    • 2021-09-14
    • 1970-01-01
    • 2019-05-27
    相关资源
    最近更新 更多