39

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.

Graham Borland
  • 60,055
  • 21
  • 138
  • 179
iForests
  • 6,757
  • 10
  • 42
  • 75
  • 1
    Could you please edit your title to better reflect your question? Having many questions called `How to write this code in a Kotlin way?` doesn't really make it easy to look up later. – marstran Dec 15 '16 at 11:34
  • @marstran Yes sure. Do you have any suggestions about the title? I cannot find a good way to explain my problem. Thanks. – iForests Dec 15 '16 at 12:35
  • Something like `How to idiomatically test for non-null, non-empty strings in Kotlin?` maybe? Basically something that describes the actual question. – marstran Dec 15 '16 at 12:38
  • Since Kotlin 1.3, isNullOrEmpty and orEmpty are available on collections, maps, and array of objects. Not just on CharSequence. (https://kotlinlang.org/docs/reference/whatsnew13.html) – aldok Dec 22 '19 at 18:46

5 Answers5

54

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
}
miensol
  • 39,733
  • 7
  • 116
  • 112
  • 4
    I would add that `if (s != null && s.isNotEmpty()) { ... }` _is_ idiomatic Kotlin for this scenario if you want to proceed to actually use `s` as non-null using smart cast. – mfulton26 Dec 15 '16 at 13:16
  • 4
    The question is not silly at all. Another possible form would be `if (s?.isNotEmpty() == true) {}` – Kirill Rakhman Dec 15 '16 at 14:16
  • @mfulton26 note that `s` was declared as `var` in which case `if (s != null && s.isNotEmpty()) ` will not compile nor will it result in smart cast. – miensol Dec 15 '16 at 18:49
  • So it was. I did not notice that before. Thank you @miensol. – mfulton26 Dec 15 '16 at 19:56
  • 1
    @miensol I notice that I get a smart cast very well even for a `var`. As long as it’s a local variable, it cannot have changed in between. Has Kotlin gotten smarter about this in the meantime? – Michael Piefel Mar 29 '17 at 07:28
  • This kind of thing only works because isNulOrEmpty is an extension function which means you can call it on null, since under the hood it gets converted to a jvm static method call with null passed in. In my opinion this is confusing because you have to keep track of which calls are extension functions and which calls are member functions. – miguel Jun 28 '17 at 20:09
  • 1
    @miguel Why do you have to keep track of that? If it was a member the code would not compile on a nullable type. No confusion imo. – rozina Jul 04 '17 at 11:28
  • It doesn't work with Android SharedPrefs comparison. – IgorGanapolsky Mar 15 '20 at 18:04
5

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.

Michael Piefel
  • 18,660
  • 9
  • 81
  • 112
  • 2
    The suggestion by @Kirill Rakhman will also produce a smart cast: `if (s?.isNotEmpty() == true) {}` – Love Apr 02 '17 at 11:47
  • 1
    That’s true. It’s a matter of personal preference here. When I see the suspicious `== true` part I immediately think that it could be removed, which it cannot in this case. You save 4 characters, but for me it’s less readable. – Michael Piefel Apr 03 '17 at 11:23
  • 6
    Note that my answer is outdated. `!s.isNullOrEmpty()` will perform a smart cast. – Michael Piefel Mar 16 '20 at 11:29
2

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")
}
mtuulu
  • 1,042
  • 1
  • 10
  • 14
2

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.

Nevercom
  • 840
  • 3
  • 14
  • 38
0

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

}
Pipo
  • 4,653
  • 38
  • 47