【问题标题】:Only safe (?.) or non-null asserted (!!.) calls are allowed on a nullable receiver of type String?在 String 类型的可空接收器上只允许安全 (?.) 或非空断言 (!!.) 调用?
【发布时间】:2018-02-27 10:23:20
【问题描述】:
fun checkLengthA(str : String?): Int = if (str.isNullOrBlank()) 0 else str.length

“String 类型的可空接收器只允许安全 (?.) 或非空断言 (!!.) 调用?

所有空对象(或空)都被 isNullOrBlank() 捕获,因此 str.length 中的 str 对象永远不能为空(或空)。这可以通过用显式检查替换扩展函数来实现。

fun checkLengthB(str : String?): Int = if (str == null) 0 else str.length

或更简洁的表达方式:

fun checkLengthC(str : String?): Int = str?.length ?: 0

checkLengthB 和 checkLengthC 都可以正常运行。

让我们从 checkLengthA 中删除可空类型以避免编译错误,上面突出显示:

fun checkLengthA(str : String): Int = if (str.isNullOrBlank()) 0 else str.length

现在,我们只允许解析使用字符串类型的非空参数,所以如果我们期望一些空类型,那么我们必须把“?”返回。

看起来编译器不理解 String 类型的 str 在运行 str.length 和扩展函数时永远不会评估为 null,但如果我们在 if-else 中使用 (str == null) 它将毫无问题地编译声明。

谁能解释为什么会这样?

【问题讨论】:

    标签: kotlin


    【解决方案1】:

    编译器无法证明在isNullOrBlank 计算结果为false 之后,参数不能为空。 isNullOrBlank 只是一个普通方法,相同的参数可以应用于任何数量的检查参数是否为空的函数。如果有人将isNullOrBlank 的实现更改为始终返回false 怎么办?

    arg == null 的情况下,编译器知道这一定意味着参数不为空,因为,这正是== null 在语法和字面上的含义。

    请注意,== null 并不总是会导致安全转换,例如,当被检查的变量是属性时。

    在这种情况下:

    str?.length ?: 0
    

    不涉及智能演员表。

    str?.lengthInt?,使用 Elvis 运算符 ?: 您最终将得到 Int


    有趣的是,the implementation of isNullOrBlank 包含这个contract

    contract {
        returns(false) implies (this@isNullOrBlank != null)
    }
    

    所以也许这会在某个时候得到支持。

    【讨论】:

    • 我的理解是 isNullOrBlank 扩展函数应该执行 str != null 检查,这样编译器就不会抱怨。我不完全理解它的作用; “合同”部分对我来说是新的。
    • @pitos 是的,确实如此,但这是在另一个函数中 - 它无法编译,因为代码 in 函数(Kotlin 标准库)可能会在某些地方发生变化点,因为函数实际上只是另一层抽象。 (它可能不会改变,因为它是如此简单,但重点仍然存在——它只是一个函数。
    • @pitos 好消息:Kotlin 1.3 will support contract(尽管自定义 contracts 是实验性的)。
    猜你喜欢
    • 1970-01-01
    • 2018-04-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-03-24
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多