如何在 Kotlin 中用惯用方式测试非空字符串?

39

我刚接触Kotlin,希望能得到帮助,将以下代码改写为更优雅的形式。

var s: String? = "abc"
if (s != null && s.isNotEmpty()) {
    // Do something
}

如果我使用以下代码:

if (s?.isNotEmpty()) {

编译器会抱怨说

Required: Boolean
Found: Boolean?

谢谢。


1
你能否编辑一下标题,更好地反映你的问题?很多问题都叫做“如何用 Kotlin 的方式编写这段代码?”这样并不容易在以后查找。 - marstran
@marstran 当然可以。你对标题有什么建议吗?我找不到一个好的方法来解释我的问题。谢谢。 - iForests
类似于“在 Kotlin 中如何习惯性地测试非空字符串?”这样的内容?基本上是描述实际问题的东西。 - marstran
自 Kotlin 1.3 开始,isNullOrEmpty 和 orEmpty 已经可以用于集合、映射和对象数组,而不仅仅是 CharSequence。(https://kotlinlang.org/docs/reference/whatsnew13.html) - aldok
5个回答

55
你可以使用 isNullOrEmpty 或其伙伴isNullOrBlank,用法如下:
if(!s.isNullOrEmpty()){
    // s is not empty
}

isNullOrEmptyisNullOrBlank 都是扩展方法,它们作用于 CharSequence?,因此您可以安全地使用它们处理 null。或者像这样将 null 转换为 false:

if(s?.isNotEmpty() ?: false){
    // s is not empty
}

你也可以做以下操作

if(s?.isNotEmpty() == true){ 
    // s is not empty
}

4
如果您想继续使用变量s,并将其视为非空类型,则 if (s != null && s.isNotEmpty()) { ... } 是在 Kotlin 中用于这种情况的惯用语。需要说明的是,这种写法符合语言习惯且易于理解。 - mfulton26
4
这个问题一点也不傻。另一个可能的格式是 if (s?.isNotEmpty() == true) {} - Kirill Rakhman
1
@miensol 我发现即使是 var,我也能得到一个很好的智能转换。只要它是一个局部变量,在此期间它就不可能已经改变了。 Kotlin 在这方面有更聪明的表现吗? - Michael Piefel
之所以这种方法可行,是因为isNullOrEmpty是一个扩展函数,这意味着您可以在null上调用它,因为在底层它会转换为传递null的jvm静态方法调用。但我认为这很令人困惑,因为您必须跟踪哪些调用是扩展函数,哪些调用是成员函数。 - miguel
1
@miguel 为什么你要追踪它呢?如果它是成员,该代码将无法在可空类型上编译。在我看来没有混淆的地方。 - rozina
显示剩余3条评论

5
尽管我非常喜欢@miensol的答案,但我的回答是(if (s != null && s.isNotEmpty()) {...})实际上是Kotlin中惯用的方式。只有这种方式才能在块内获得对String的智能转换,而使用所接受的答案的方法将不得不在块内使用s !!。

2
@Kirill Rakhman的建议也会产生一个智能转换:if (s?.isNotEmpty() == true) {} - Love
1
没错。这是个人喜好的问题。当我看到可疑的 == true 部分时,我立即认为它可以被删除,但在这种情况下不行。你可以节省 4 个字符,但对我来说可读性会降低。 - Michael Piefel
6
请注意,我的回答已经过时。!s.isNullOrEmpty() 将执行智能转换。 - Michael Piefel

2
或者创建一个扩展方法并将其用作安全调用:
fun String?.emptyToNull(): String? {
    return if (this == null || this.isEmpty()) null else this
}

fun main(args: Array<String>) {
    val str1:String?=""
    val str2:String?=null
    val str3:String?="not empty & not null"

    println(str1.emptyToNull()?:"empty string")
    println(str2.emptyToNull()?:"null string")
    println(str3.emptyToNull()?:"will not print")
}

1
Kotlin语言中是否提供类似的功能? - IgorGanapolsky

2

或者您可以创建一个扩展函数

public inline fun String?.ifNotEmpty(crossinline block: (String) -> Unit): Unit {
    if (this != null && this.isNotEmpty()) {
        block(this)
    }
}

查看演示

我认为这种方法在上下文中与 UI 工具包 Jetpack Compose 更易于阅读。

handles.twitter.ifNotEmpty {
    SocialMediaItem(handle = it, type = SocialMedia.TWITTER)
}

在这种情况下,意图是仅在 Twitter 句柄不为空且不是空字符串时显示该 UI 块。

0

另一个扩展选项。 我正在寻找一种验证一堆字符串并使用它们的方法,而嵌套块在这方面阅读起来很麻烦。

fun String?.notNullOrEmpty(illegalArgumentExceptionMsg: String): String {
    return if (this == null || this.isEmpty()) throw IllegalArgumentException(illegalArgumentExceptionMsg) else this
}

简单使用

    val myNullOrEmptyString: String? = "bar"
    val myNotNullString = myNullOrEmptyString.notNullOrEmpty("myNullOrEmptyString should not be empty")

使用
    val myNullOrEmptyString: String? = "bar"
    val myNotNullString: String = myNullOrEmptyString.notNullOrEmpty("myNullOrEmptyString should not be empty")

测试或使用

@Test
fun emptyNotNullExtension() {
    val msg = "foo"
    assertThatThrownBy {
        val emptyNotNullString: String = "".notNullOrEmpty(msg)
    }
        .isExactlyInstanceOf(IllegalArgumentException::class.java)
        .hasMessageContaining(msg)
    assertThatThrownBy {
        val emptyNotNullString: String = null.notNullOrEmpty(msg)
    }
        .isExactlyInstanceOf(IllegalArgumentException::class.java)
        .hasMessageContaining(msg)

    val myNullOrEmptyString: String? = "bar"
    val myNotNullString: String = myNullOrEmptyString.notNullOrEmpty("myNullOrEmptyString should not be empty")

}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接