18

Is it possible to write something like this, or do we have to revert back to manual null checking in Kotlin?

val meaningOfLife : String? = null

when meaningOfLife {
    exists -> println(meaningOfLife)
    else   -> println("There's no meaning")
}
TomTom
  • 2,820
  • 4
  • 28
  • 46
  • the `if` construct is more preferable for binary situations like this one. Has the exact same effect as `when` here – voddan Jun 04 '16 at 05:49
  • Why is it preferrable if it has the same effect? The advantage of `when` is that it has room to grow. – TomTom Jun 04 '16 at 12:13
  • In case of nullability `when` has nothing to grow for. Using `when` as a glorified `if` is quite wasteful IMO – voddan Jun 04 '16 at 12:53
  • 2
    It's only wasteful if `when` desugars to something that uses more resources than a plain `if`. If not, then you have one less line and conditional expandability in contrast to `if`. – TomTom Jun 04 '16 at 18:08
  • When I see `when`, I prepare myself to a multy-chose. So it would surprised me to find only a bi-chose. – voddan Jun 04 '16 at 19:08
  • 1
    It is only a matter of taste. Fortunately Kotlin allows us to do what we think is necessary – voddan Jun 04 '16 at 19:09

4 Answers4

20

One of possible ways is to match null first so that in else branch the String? is implicitly converted to String:

val meaningOfLife: String? = null

when (meaningOfLife) {
    null -> println("There's no meaning")
    else -> println(meaningOfLife.toUpperCase()) //non-nullable here
}

This is a special case of a smart cast performed by the compiler.

Similar effect can be achieved with is String and else branches -- is String-check is true when the value is not null.

For more idioms regarding null-safety please see this answer.

Community
  • 1
  • 1
hotkey
  • 140,743
  • 39
  • 371
  • 326
  • 1
    Making `is String` the first case achieves a similar effect. – EPadronU Jun 03 '16 at 16:32
  • 2
    @EPadronU, not completely: as seen from bytecode perspective, `is String` causes `instanceof` check, while the code in the answer doesn't, it only compares the reference with `null`. – hotkey Jun 03 '16 at 17:02
  • Thank you. That's very useful to know. Fortunately, I said "similar" ;) – EPadronU Jun 03 '16 at 17:13
  • Unfortunately the smart cast only works if `meaningOfLife` is immutable. There's no way around `meaningOfLife!!.function()` when it's mutable, is it? – TomTom Jun 04 '16 at 12:10
  • @TomTom, the compiler cannot guarantee null-safety in case of `var`, so there's indeed no other way but to create another local `val`. But when a variable is non-nullable and you only need smart cast for `var` you can write something like `myVar.let { when (it) { /*smart cast works here*/ } }`. – hotkey Jun 04 '16 at 12:16
13

You can accomplish that as follows:

val meaningOfLife: String? = null

when (meaningOfLife) {
  is String -> println(meaningOfLife)
  else -> println("There's no meaning")
}
EPadronU
  • 1,783
  • 1
  • 15
  • 15
2

FYI, the particular situation in the question has a way simple solution with the ?: operator of default value:

println(meaningOfLife ?: "There's no meaning")

The example in the question is probably a simplified real situation, so a null check is how I would do it. IMHO if is a better way to go when you have a binary chose of control flow:

if(meaningOfLife != null)
    println(meaningOfLife)
else
    println("There's no meaning")

Takes exactly the same number of lines, BTW.

voddan
  • 31,956
  • 8
  • 77
  • 87
  • I understand the question was probably not about it, but I think this option should also be mentioned – voddan Jun 04 '16 at 05:57
0

Try this:

val meaningOfLife : String? = null    
meaningOfLife?.let { println(it) } ?: println("There's no meaning")

let in stdlib

Vasiliy_N
  • 11
  • 2
  • 2
    OP wanted to know how to use pattern matching to do it. Anyways, I think your expression could be cleaned up to this: `println(meaningOfLife ?: "There is no meaning")` – marstran Jun 03 '16 at 12:56