【问题标题】:Kotlin extension function - compiler cannot infer that nullable is not nullKotlin 扩展函数 - 编译器无法推断 nullable 不为 null
【发布时间】:2020-01-18 00:57:01
【问题描述】:

假设我有一个简单的类Foo 和一个可以为空的String?

data class Foo(
    val bar: String?
)

我创建了一个简单的函数capitalize

fun captitalize(foo: Foo) = when {
    foo.bar != null -> runCatching { foo.bar.capitalize() }
    else -> ""
}

这很好,因为编译器推断 foo.bar 不能为空,即使它的类型是可空的。但后来我决定编写与Foo的扩展相同的函数

fun Foo.captitalize2() = when {
    bar != null -> runCatching { bar.capitalize() }
    else -> ""
}

突然之间,编译器不再能够推断 bar 不为空,而 IntelliJ 告诉我“只允许对可空值进行安全 (?.) 或非空断言 (!!.) 调用字符串类型的接收者?”

谁能解释一下原因?

【问题讨论】:

    标签: kotlin nullable kotlin-extension


    【解决方案1】:

    我认为这是因为在第一种情况下您正在调用此函数:

    public inline fun <R> runCatching(block: () -> R): Result<R> {
        return try {
            Result.success(block())
        } catch (e: Throwable) {
            Result.failure(e)
        }
    }
    

    但在第二种情况下,您使用接收器调用函数:

    public inline fun <T, R> T.runCatching(block: T.() -> R): Result<R> {
        return try {
            Result.success(block())
        } catch (e: Throwable) {
            Result.failure(e)
        }
    }
    

    对我来说,这似乎是 Kotlin 编译器中的一个问题,因为如果您自己内联此函数的代码,它将正常工作:

    fun Foo.captitalize2() = when {
        bar != null -> try {
            Result.success(bar.capitalize())
        } catch (e: Throwable) {
            Result.failure<String>(e)
        }
        else -> ""
    }
    

    顺便说一句,如果我是你,我想这样写我的 capitalize2 函数:)

    fun Foo.captitalize2() = bar?.capitalize() ?: ""
    

    【讨论】:

    • 正如@Andrei Tanana 所建议的,您在capitalize2 中的runCatching 调用是对public inline fun &lt;T, R&gt; T.runCatching(block: T.() -> R) 的调用:结果 ` 和类型参数被推断为runCatching&lt;Foo, String&gt;T 对于编译器来说是Foo,而不是Foo having not null property bar,所以你会得到编译错误......当你内联函数时,when 之间没有类型推断和bar.capitalize(),所以编译器很高兴,你没有错误......
    【解决方案2】:

    所以,最后我找到了一种替代方法,它允许我们使用runCatching 而不会出现您显示的问题。 正如我对@Andrei Tanana 答案的评论,在你的代码类型参数fun &lt;T, R&gt; T.runCatching(block: () -&gt; R) : Result&lt;R&gt; 被推断为&lt;Foo, String&gt; 并且编译器不能使用this.bar 不是null 的信息。

    如果你重写capitalize2函数如下

    fun Foo.capitalize2(): Serializable = when {
        bar != null -> bar.runCatching { capitalize() }
        else -> ""
    }
    

    T 被推断为String(感谢when 表达式的bar != null 情况)并且编译器不会抱怨传递给的 中的this.capitalize() 调用runCatching.

    我希望这可以帮助您,既可以作为解决问题的方法,也可以作为对问题本身的解释。

    【讨论】:

      猜你喜欢
      • 2019-05-01
      • 2011-05-07
      • 1970-01-01
      • 1970-01-01
      • 2015-06-28
      • 2020-01-21
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多