When is it appropriate to use the `!!` operator? E...
# announcements
f
When is it appropriate to use the
!!
operator? Example: In an edit and delete situation I never expect the
todoId
to be null, only when a new todo was added. Does it make sense to just use
!!
then instead of the null-safe operator?
Copy code
fun onAddEditResult(result: Int, todoId: Long? = null) {
        viewModelScope.launch {
            when (result) {
                RESULT_TODO_ADDED -> eventChannel.send(Event.ShowTodoSavedConfirmationMessage("Todo added"))
                RESULT_TODO_EDITED -> {
                    eventChannel.send(Event.ShowTodoSavedConfirmationMessage("Todo updated"))
                    setExpandedState(todoId!!, false)
                }
                RESULT_TODO_DELETED -> {
                    val todo = todoDao.getTodoById(todoId!!)
                    todo?.let {
                        todoDao.delete(todo)
                        setExpandedState(todo.id, false)
                        eventChannel.send(Event.ShowUndoDeleteTodoMessage(todo))
                    }
                }
            }
        }
    }
e
seems like this would be better modeled as a
Copy code
sealed class Todo {
    object Added : Todo()
    data class Edited(val todoId: Long) : Todo()
    data class Deleted(val todoId: Long) : Todo()
}
then you can't have such inconsistency between input arguments
👍 1
f
but I'm getting the result as an Int flag because it's sent over screens
y
You can use a custom Parcelable implementation then to store those classes as different values
f
as far as I know, Parcelable is not supported by default in Compose and I don't want to hack something together
y
Ohhhh it's compose. Can you only send Ints through? Because if you can send Strings, you could use kotlinx-serialization to serialize those classes
f
sorry I forgot that this is not the Compose channel
yea I can send a string but is my approach really so bad?
Wait, a sealed class might work. Let me try it out.
y
I would say it isn't bad per se like using
!!
here is absolutely acceptable. What the people here and I are getting at though is that isn't expandable easily and it requires the developer working on the code to know that this long can only be those 3 values and that if it is those 2 values then the other long cannot be null etc. Simply, it just seems like it isn't documented based on the types. If this is jsut a personal hobby project, then sure this works. But if it was for something more professional or if you want to do it "The Right Way" then using a sealed class is best
f
@Youssef Shoaib [MOD] Thanks for the explanation. It totally makes sense to me now!
The problem is I'm sending this result between screens, through the SavedStateHandle in Compose, which only supports primitive types. I could send it by wrapping it into LiveData but that feels hacky. I think we are meant to send primitive types between screens only.
serializing it into a string also feels weird
y
If it is through Saved State, then AFAIK it saves it into a bundle, which supports Parcelable. Lemme check really quickly though
f
@Youssef Shoaib [MOD] yea I think that works 👍
I think Compose doesn't accept Parcelables for navigation arguments but the SavedStateHandle does
y
Yeahhh and worst case scenario you can pass the serialized object as JSON
f
I implemented it as a sealed class now and I like it 👍
thanks again
e
To answer your original question,
!!
is appropriate wherever you decide it is. For example, on my team I've decided that it's almost never appropriate because it's usually indicative that something was designed wrong. It's allowed if it's deep in a 3rd party integration, and even then only if there's no feasible way to refactor.
f
alright, thank you @eygraber
c
@Florian I asked this question a while back and got some good responses from Roman Elizarov and Adam Powell. The two best things I took out of that question was 1. It's okay to use !! when you basically mean that "if this is null, then something in the universe is wrong" Basically you're okay if the app crashes because it is truly unexpected 2. I think adam powell basically equated it to squeezing a tube of toothpaste. You can work with nullable data types but you hopefully squeeze them into being something more concrete as data flows through different layers of your app. Overall, I was basically able to take the stance that "!! is not evil like some people make it out to be" I'll try to search for the convo
Found both! Really good reads. You can see that some people come out strongly against it, but it was nice to see someone like Roman say that it's a perfectly fine way to require not null https://kotlinlang.slack.com/archives/C0922A726/p1570446700141800 https://kotlinlang.slack.com/archives/C0B8M7BUY/p1601007223034000
f
@Colton Idle Thank you. Yea I also thought the same way. If the value should really never be null then crashing is probably better than ignoring it.
👍 1
c
Jake Wharton also has some very very good tweets on this topic if you feel like using twitter advanced search.
He taught me what an invariant was. 😄
e
Using it as an assert can lead to some difficulty with debugging in certain scenarios (I tend to use it on scripts out throwaway projects a lot despite not allowing it in my main project).
requireNotNull
helps a lot with debugging, although it can't be used the same way as
!!
e
checkNotNull()
and
requireNotNull()
both return the non-null value, so they can be used like
!!
, but allow you to attach a message as well
they also work with smart-casting like
!!
does, so I'm not sure what you mean by "it can't be use in the same way as `!!`"?
e
Sorry, I could've been clearer with that. I meant that it's a bit more awkward to use inline than
!!
e
if you need it in postfix position,
.let { checkNotNull(it) }
could work, but it is pretty long and awkward. can't compare to the succinctness of
!!
, that's for sure