Home > Software engineering >  Can I smartCast within the when statement?
Can I smartCast within the when statement?

Time:11-19

I have the code below

class Sample {

   var variable1: SomeClass? = null
   var variable2: SomeClass? = null
   var variable3: SomeClass? = null


   fun checkVariable() {
      when {
         variable1 != null -> variable1!!.doSomething()
         variable2 != null -> variable2!!.doSomething()
         variable3 != null -> variable3!!.doSomething()
      }
   }

}

I'm hoping I can make variableX after the -> non-nullable, so I don't need to !!. I can avoid !! with

variable1 !== null -> variable1?.doSomething()

But is there away to do this more elegantly that I can have a non-nullable variable to access the doSomething()?

CodePudding user response:

The error is not because of the when. You just cannot smart cast class-level vars.

In this case, since all your variables are of the same type and you are doing the same thing on all of them, your code can be simplified to:

(variable1 ?: variable2 ?: variable3)?.doSomething()

In other words, you are finding the first non-null out of the three variables, and calling doSomething on it.

If your variables are not of the same type, and you are doing different things to each of them, you can do:

variable1?.also {
    it.doSomething()
} ?: variable2?.also {
    it.doSomethingElse()
} ?: variable3?.also {
    it.doAnotherThing()
}

CodePudding user response:

Not sure if it's more elegant but you could maybe write this instead:

fun getStrLength() = (variable1 ?: variable2 ?: variable3)?.doSomething()


Another way to remove the !! is to first store them in local variables like

fun getStrLengths() {

    val variable1 = variable1
    val variable2 = variable2

    val variable3 = variable3
 
    when {

        variable1 != null -> variable1.doSomething()

        variable2 != null -> variable2.doSomething()

        variable3 != null -> variable3.doSomething()

    }
}

CodePudding user response:

Lazy initialisation

You could make the properties non-nullable with lateinit var.

This would prevent the need for any null checks.

You can check to see if the properties are present with isInitialized instead of a non-null check.

class Sample {
  lateinit var variable1: SomeClass
  lateinit var variable2: SomeClass
  lateinit var variable3: SomeClass

  fun checkVariable() {
    when {
      // so long as a value is initialised, there is no need for null checks
      ::variable1.isInitialized -> variable1.printName()
      ::variable2.isInitialized -> variable2.printName()
      ::variable3.isInitialized -> variable3.printName()
    }
  }
}

class SomeClass(val name: String) {
  fun printName() {
    println(name)
  }
}

Unsetting values

This can be useful to avoid null checks, but it would prevent 'unsetting' previously set variables with null.

val sample = Sample()

sample.variable1 = SomeClass("foo")

sample.variable1 = null // ERROR: Null can not be a value of a non-null type SomeClass

Whether unsetting values is required or not depends on your use-case.

Example

fun main() {

  val sample = Sample()

  println("first check:")
  sample.checkVariable()
  println("---")

  sample.variable3 = SomeClass("Jamie")

  println("second check:")
  sample.checkVariable()
  println("---")

  sample.variable2 = SomeClass("Maddie")

  println("third check:")
  sample.checkVariable()
  println("---")

  sample.variable1 = SomeClass("Lisa")

  println("fourth check:")
  sample.checkVariable()
  println("---")
}

We can see that as each variable is set, other variables are not called, as they are listed lower in the when statement.

first check:
---
second check:
Jamie
---
third check:
Maddie
---
fourth check:
Lisa
---
  • Related