1

I have an item with variable datePurchased, which can be null. Based on purchase date, I generate a label. When I check if datePurchased is null, in else branch I still have to check for null. It says that smart cast is impossible, because it's a mutable property.

Here's what I've tried so far:

if (datePurchased == null) {
    ""
} else {
    if (datePurchased.isToday()) {//error here
    }
}

    when {
        datePurchased == null    -> {

        }
        datePurchased.isToday() -> {//smart cast bla bla mutable bla bla
        datePurchased?.isToday() -> {//expected Boolean, got Boolean?
        datePurchased?.isToday()?:false -> {//all good, but does not look nice, since datePurchased can't be null here
        }
        else                     -> {

        }
    }
Lior Bar-On
  • 10,784
  • 5
  • 34
  • 46
Marius Kaunietis
  • 674
  • 4
  • 17

3 Answers3

4

Thanks to marstran, I ended up with such solution:

        return datePurchased?.let {
            when {
                it.isToday()     -> {
                    "Today"
                }
                it.isYesterday() -> {
                    "Yesterday"
                }
                else             -> {
                    dateFormat.format(it)
                }

            }
        } ?: ""
Marius Kaunietis
  • 674
  • 4
  • 17
0

If you are confident you have no data race in which datePurchased becomes null, then add a non-null assertion in the else branch:

if (datePurchased == null) {
    ""
} else {
    datePurchased!!.isToday()
}

Or shorter and more reliable:

datePurchased?.isToday() ?: ""
voddan
  • 31,956
  • 8
  • 77
  • 87
0
  1. datePurchased is mutable, which means it can be changed.

  2. Your code is not running inside any sort of synchronous lock, which means another thread could potentially be running and modifying it at the same time.

With this in mind, the following is possible:

if (datePurchased == null) {
    ...
} else {

    // another thread comes in here and changes datePurchased=null

    if (datePurchased.isToday()) { // Null Pointer Exception!
        ...
    }
}

You may have no thread doing this, but the compiler doesn't know. It plays it safe and says you can't do this. Probably 98% of the time it's wrong but that other 2% forces you to think about how your code behaves in a concurrent environment.

One solution is to simply use a local val that can't be changed in a new thread:

val datePurchased = datePurchased

if (datePurchased == null) {
    ...
} else {

    // datePurchased val cannot have been changed, compiler can guarantee safety

    if (datePurchased.isToday()) { 
        ...
    }
}

But the main thing is you now need to think about what immutable really means in the context of your app, and whether you really need to have variables mutable.

Tom
  • 6,946
  • 2
  • 47
  • 63