Not-null assertion operator(!!) is used when you are completely certain that a variable is not null. It can be useful for example to avoid additional checks with if statement or let() function. If you have at least a little doubt use safe calls(?.).
Not-null assertion operator(!!) is also used for interoperability with the Java language. For example if some Java library returns an object, Kotlin considers it as nullable, but if you are certain and library docs says that the object can't be null, so you can use Not-null assertion operator(!!).
Videos
Not-null assertion operator(!!) is used when you are completely certain that a variable is not null. It can be useful for example to avoid additional checks with if statement or let() function. If you have at least a little doubt use safe calls(?.).
Not-null assertion operator(!!) is also used for interoperability with the Java language. For example if some Java library returns an object, Kotlin considers it as nullable, but if you are certain and library docs says that the object can't be null, so you can use Not-null assertion operator(!!).
Suppose you are fetching some data with api call of Movie data (movie_name, language, length, subtitles), and you are showing that info in list, if variable life subtitles is not present or having null value, you can use movie?.subtitles to variable if its null or not before you assign it to any text view,
For !! Operator, Suppose you define one Multithreading task where one variable like reader is running, if that is null at runtime then you can throw runtime exception
The compiler can’t know that body() is guaranteed to return the same thing each time so it can’t smart-cast it to non-null. Also, it doesn’t know anything as complex as the fact that the body won’t be null if isSuccessful is true.
Since we know from the documentation of this library that it will always be the same, it’s safe to use !!. But you could alternatively copy the value to a local variable and work with that variable in your null check. Then you could be sure your code is safe even if you misinterpreted the documentation.
I don't really use Retrofit myself, but from a little bit of reading about it, I know that you can work with it more naturally in Kotlin by using suspend functions, and then you wouldn't have to work with callbacks and Response objects. So instead of creating a function that returns a Call<MyData>, followed by enqueuing the call, listening for success/failure, and then unwrapping the body if it's successful; instead you can mark the function as suspend and return MyData directly. Use try/catch or runCatching when making the function call, because it throws when not successful.
You need to set response.body() to a variable and then check for its nulability:
val body = response.body()
if (response.isSuccessful && body != null) {
val result = body.result
}
Unfortunately, the bodies of the functions that you call, including inline functions, are not used for smart casts and nullability inference.
There's not much in your code that can be improved, and I would only suggest one thing: you can use the Elvis operator with a Nothing function for those assertion statements. The control flow analysis takes into account the branches resulting into Nothing and infers nullability from that:
fun failOnNull(): Nothing = throw AssertionError("Value should not be null")
val test: Foo? = foo()
test ?: failOnNull()
// `test` is not-null after that
This can be written without a function as well: test ?: throw AssertionError("..."), because a throw expression also has type Nothing.
Speaking of a more general case of failing an assertion, one might use a fail(...): Nothing function, which provides a bonus hint for the control flow analysis as well. The JUnit Assert.fail(...) is not a Nothing function, but you can find one in the kotlin-test-junit module or write your own one.
test as? SomeType ?: fail("`test` should be an instance of SomeType")
// smart cast works here, `test` is `SomeType`
The kotlin.test library comes with an easy solution for this:
kotlin.test.assertNotNull()
Because this function implements Kotlin contracts it supports smart casting:
contract { returns() implies (actual != null) }
Example:
fun Foo?.assertBar() {
assertNotNull(this)
assertEquals(this.bar, 0)
}
Just make sure to use the right assertNotNull import (import kotlin.test.assertNotNull)!
If you don't already use the kotlin.test library, add it to your project:
group: 'org.jetbrains.kotlin', name: 'kotlin-test', version: '1.3.11
It seems you declare testingStartTime and testingEndTime as mutable variables (var), so smart cast to not-nullable type can't be done. There are two idiomatic ways to fix your problem:
Declare testingStartTime and testingEndTime as immutable variables (val) (if possible)
Create two local immutable copies of these variables and work with them to make smart cast possible:
val startTime = testingStartTime
val endTime = testingEndTime
if ((startTime != null) && (endTime != null))
summary.duration = endTime.time - startTime.time
You can do it like:
summary.duration = testingEndTime?.let { endTime ->
testingStartTime?.let { startTime ->
endTime - startTime
}
}
Which would work, but would assign null to summary.duration if either testingEndTime or testingStartTime are null. If you wanted a default value for when either of the dependant values is null then you could do:
summary.duration = testingEndTime?.let { endTime ->
testingStartTime?.let { startTime ->
endTime - startTime
}
} ?: 0
Or, if you don't want to change the value of summary.duration at all if either of the dependant values are null you can do:
summary.duration = if (testingEndTime != null && testingStartTime != null) {
testingEndTime - testingStartTime
} else {
summary.duration
}