Is it possible to make safe inline Optional in Kotlin?

穿精又带淫゛_ 提交于 2020-01-13 06:39:08

问题


In Kotlin sometimes I have to work with double nullability. For example, I need double nullability, when I want to use T? where T may be a nullable type. There are a few approaches for doing this:

  1. Holder<T>? where Holder is data class Holder<out T>(val element: T) - example1
  2. boolean flag variable - example1
  3. containsKey for Map<K, T?> - example1
  4. The special UNINITIALIZED_VALUE for representing the second kind of null - example1

The last approach has the best performance, but it's also the most error-prone. So I've decided to encapsulate it in 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()
}

The first problem with this Optional is public constructor, which allows creating instances with arguments of not matching type.

The second problem was noticed at testing time. Here is the failed test:

emptyOr { Optional.EMPTY }.value assertEql null
fun <T> emptyOr(other: () -> T): T = Optional.EMPTY.or(other)

Exception:

Exception ClassCastException: Optional$NULL cannot be cast to Optional
    at (Optional.kt:42) // emptyOr { Optional.EMPTY }.value assertEql null

If I remove inline modifier from Optional, the test will pass.

Q: Is there any way to fix these problems without removing inline modifier from Optional?


1 Examples include some context. Please read them fully before writing that I added incorrect links.

来源:https://stackoverflow.com/questions/58256697/is-it-possible-to-make-safe-inline-optional-in-kotlin

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!