iForests
iForests

Reputation: 6949

How to idiomatically test for non-null, non-empty strings in Kotlin?

I am new to Kotlin, and I am looking for help in rewriting the following code to be more elegant.

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

If I use the following code:

if (s?.isNotEmpty()) {

The compiler will complain that

Required: Boolean
Found: Boolean?

Thanks.

Upvotes: 43

Views: 31189

Answers (5)

Pipo
Pipo

Reputation: 5093

Another extension option. I was looking for a way of validating a bunch of strings and then using them, and the nested blocks are a pain for reading in that aspect.

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

simple use

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

use

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

Test or uses

@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")

}

Upvotes: 0

Nevercom
Nevercom

Reputation: 869

Or you can create an Extension Function:

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

See it in action

I find this approach more readable in context of Jetpack Compose which is a UI Toolkit.

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

In this case the intention is to only display that block of UI if Twitter handle is not null, and is not an empty string.

Upvotes: 3

miensol
miensol

Reputation: 41678

You can use isNullOrEmpty or its friend isNullOrBlank like so:

if(!s.isNullOrEmpty()){
    // s is not empty
}

Both isNullOrEmpty and isNullOrBlank are extension methods on CharSequence? thus you can use them safely with null. Alternatively turn null into false like so:

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

you can also do the following

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

Upvotes: 60

mtuulu
mtuulu

Reputation: 1072

or create an extension method and use it as a safe call:

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")
}

Upvotes: 2

Michael Piefel
Michael Piefel

Reputation: 19998

Although I like the answer by @miensol very much, my answer would be (and that is why I do not put it in a comment): if (s != null && s.isNotEmpty()) { … } actually is the idiomatic way in Kotlin. Only this way will you get a smart cast to String inside the block, while with the way in the accepted answer you will have to use s!! inside the block.

Upvotes: 6

Related Questions