Home > Software design >  How to use force unwrap correctly?
How to use force unwrap correctly?

Time:10-07

There is a disscussion here that force unwrap should be avoided as much as possible almost always you can, however, I think it is not always like this - I would say you can use force unwrap if you sure about that value won't be null and it is kind of intention to show others that you don't expect null at all here or there. You don't need to put ? whenever is possible and put it without any thought just because you can.

Let's take such an example

class TestDeleteIt {
    var someImplementation: SomeImplementation? = null
    
    init {
        createImplementation()
        startQuery()
    }
    
    fun createImplementation() {
        someImplementation = SomeImplementation()
    }
    
    fun releaseImplementation() {
        someImplementation?.let {
            it.releaseRes()
        }
        
        someImplementation = null
    }
    
    private fun startQuery() {
        someImplementation!!.doQuery()
    }
}

Here I have an object of SomeImplementation which can be created and released and I have such a function startQuery() where I use force unwrap intentionally to show that here the object of someImplementation couldn't be null and we don't expect it to be null here at all. To put it differently I would like to say that this object in that line couldn't be null

I found such an article about this topic

https://agrawalsuneet.github.io/blogs/safe-calls-vs-null-checks-in-kotlin/

Where it says

But if you are sure that the var property value is not null use !! instead of ?.

However, I would like to get another opinions (sources/best practices) about this

P.S. the example I posted above may not reflect exactly the meaning, however, I hope you got the idea.

CodePudding user response:

It took me a minute to realize you mean the non-null assertion operator. I've never heard it called "force unwrap".

Yes, there are some cases where you can very carefully manually ascertain that something should never be null at the time you used the !! non-null assertion operator. This is what you are free to always do in Java, and it's why NullPointerExceptions are a big problem in Java.

I've been using Kotlin for a few years and have only found a few situations where the design is cleaner when you can assert a non-null (although I typically use ?: error() instead of !! so error messaging is better than with an NPE).

These sitations are either:

  1. You know that a private Map has a value for a key. However, there is a getValue function that provides better error messaging than []!!, so I use that instead.

  2. You have to be able to set a property to null to free that reference to the GC, but for most of the lifecycle of the class, the object is known to never be null. It would be unwieldly to have to do null-checks at every use site and handle the possibility of null when it can logically never happen.

For other cases, I always seem to find that if I rethink the redesign so nullability never has to be reasoned about and non-null asserted, it ends up being cleaner and more concise as well.

Your example is like number 2. If you accidentally allowed a null through to startQuery you may prefer the app to fail fast rather than silently behave incorrectly as it would with a null-safe ?. call. But I would say this would be preferable so if you did make that mistake, it's easier to track down:

private fun startQuery() {
    someImplementation?.doQuery() 
        ?: error("someImplementation must never be null when startQuery() is called")
}

There is a pattern for number 2 that is suggested in the Android documentation. Use a second property with no backing field that does the null assertion and can provide a clean error message. Then use that property at all the places in your lifecycle where you know that property will not be null. This provides concise, readable code at all the use sites, at the expense of you having to be sure you only use it at certain parts of a class's lifecycle. Here's an example:

private var _someImplementation: SomeImplementation? = null
private var someImplementation: SomeImplementation 
    get() = _someImplementation 
        ?: error("someImplementation must not be used during this state of the class")
  • Related