What happens if we pass `null` data to a #NonNull annotated object? - android

I have converted my old java model class to kotlin data class. Some of objects are annotated with #NonNull in java. My question is if null is passed from our backend in my data class what will happen? Does making this username nullable can help in preventing crash if null is passed?
Java code:
public abstract class Comment(){
#NonNull
public abstract String username();
}
Kotlin code:(what happen in this case if null is passed?)
data class Comment(val username: String)
Kotlin code:(it can handle null)
data class Comment(val username: String?)

In java - everything will compile and give a warning
In kotlin - your compiler won't let you pass null to nullable or #notnull annotated type
For example:
public static boolean isUserLoggedIn(#NotNull ctx: Context) {
return ...
}
// Kotlin Invocation
fun main(args: Array<String>) {
isUserLoggedIn(null)
}
And compilation error:
e: C:\_projects\test.kt: (37, 37): Null can not be a value of a non-null type Context
:app:compileDebugKotlin FAILED
FAILURE: Build failed with an exception.
In Java you are able to call this java-method with no compile error but your IDE should show warning (passing null to parameter annotated as #notnull).
Also, in Java you can pass null parameters to notnull kotlin methods. It'll compile and give a warning.
Kotlin supports some of annotations (like JetBrains, Android, Eclipse). The full list can be found here: https://kotlinlang.org/docs/reference/java-interop.html#nullability-annotations
Edit 1 - regarding the comment:
It depends if runtime null check is enabled or not. Kotlin, to ensure null safety in generated code adds kotlin.jvm.internal.Intrinsics.checkNotNull call.
See: https://github.com/JetBrains/kotlin/blob/master/libraries/stdlib/jvm/runtime/kotlin/jvm/internal/Intrinsics.java
If value is null NPE will be thrown. So, NPE will be thrown every time null is passed. Every time, even if your code could handle null value.
But, you can disable this check. Then, your code will be small lighter, and also won't throw exception every time null is passed. But you will lose a lot of profits from null safety, and it's also shows that something is bad in your design.
See how: Disable not null checks in Kotlin

Kotlin type system tells a nullable type from a not-nullable type. A declaration like x: String? means null is allowed (same as it was in Java)
The declaration x: String means you do not accept nulls. Kotlin compiler takes care of it and it will try it's best to discard any incorrect code, that tries setting null there.
Kotlin compiler understands annotations like #Nullable or #NotNull: see the documentation for more details
https://kotlinlang.org/docs/reference/java-interop.html#nullability-annotations
Of course, there are ways to call a nun-nullable method with null value (e.g. via Java Reflection or just from another JVM language). To protect from that, Kotlin Compiler emits null checks automatically, and the code will fail-fast.

Related

Argument might be null for getSharedPreferences().getString(name, default) with nonNull default string?

I don't understand why Android Studio is not able to tell that, although SharedPreferences declares the defValue of getString as #Nullable, the current value is actually not null!
The result of the following call:
myFunction(getSharedPreferences().getString(MY_SETTING_NAME.name(), "This string is not null"))
will trigger a warning:
Argument might be null
How can it be? Since defValue is actually not null and we know it...
The Android framework does not use Kotlin contracts and cannot change the #Nullable annotation on the return value based on whether the defValue you pass is null or not.
As an alternative, you should consider using Kotlin's elvis operator and writing code such as:
myFunction(getSharedPreferences().getString(MY_SETTING_NAME.name(), null)
?: "This string is not null")
Which correctly evaluates as non-null.

Make Android Lifecycle Observer receiver non nullable in Kotlin

I have Result wrapper that wraps data comes from backend
data class Result<T>(val success: Boolean, val result: T?, val message: String?)
Idea of this, check success instead of result being null or not valid and get formatted message for UI error reporting. But when trying to use this with android lifestyle components, specifically in Observer I have to check for null.
How can I avoid this null check? This happens because of
void onChanged(#Nullable T t);
in Observer. I've tried to extend this but it seem to require more custom wrapper classes. Do we have a solution for avoid null check here.
It's a framework bug that argument is annotated as #Nullable. Fixed in androix.lifecycle 2.0.0-beta01.
Updated answer from #Andrei Vinogradov's answer
Until you upgrade to 2.0.0-beta01, you can try this solution. Use standard function let from Kotlin library :
it?.let{ result ->
if(result.success){
// Rest of your code ..
}
}

Why intent.getParcelableExtra don't return nullable type in kotlin?

I have an example of extracting data from intent
val screen = intent.getParcelableExtra<Screen>("screen")
The result I received is a not nullable variable, but it can be null because I don't know if that parcelable has been added to extras. Why kotlin doesn't return a nullable type?
Kotlin just calls getParcelableExtra() as it is defined in in the android SDK in java. This method is not annotated with #Nullable so kotlin doesn't know it might return null.
Also see nullability annotations:
Nullability annotations
Java types which have nullability annotations
are represented not as platform types, but as actual nullable or
non-null Kotlin types. The compiler supports several flavors of
nullability annotations, including:
JetBrains (#Nullable and #NotNull from the org.jetbrains.annotations
package)
Android (com.android.annotations and android.support.annotations)
In the code
val screen = intent.getParcelableExtra<Screen>("screen")
the inferred type of screen will be Screen!. The exclamation mark indicates that this is a platform type, which means that it may or may not be nullable, but the compiler won't enforce null-checking behavior.
Read more: Null Safety and Platform Types
If you actively want Kotlin to enforce null safety, though, you can specify the type explicitly:
val screen: Screen? = intent.getParcelableExtra<Screen>("screen")
By specifying the type explicitly, Kotlin will give you all the compile-time null safety you're used to.

Null property when accessing lazy non-nullable property of Android Sensor

When initializing a val of type Sensor by lazy the compiler ignores that the value returned can be null
Note: The val is declared as Sensor and not Sensor?
private val sensorManager by lazy { getSystemService(Context.SENSOR_SERVICE) as SensorManager }
private val proximitySensor: Sensor by lazy { sensorManager.getDefaultSensor(Sensor.TYPE_PROXIMITY) }
sensorManager.getDefaultSensor(Sensor.TYPE_PROXIMITY) can also return null and the method is not annotated with #Nullable So, it can't be checked at compile-time.
Also, the Android Studio gives a warning when checking for null as we have not declared it as Sensor?
Shouldn't it raise a compile time error when assigning a nullable to a val which is non-null?
Resolved
When the value proximitySensor is accessed and if it is null, it produces a runtime exception as expected.
'java.lang.String android.hardware.Sensor.getName()' on a null object reference
lazy() in the Kotlin standard library reference as follows:
lazy() returns a Lazy<T> instance that stored lambda initializer.
The first call of getter executes a lambda passed to lazy() and
stores its result.
Subsequently, the getter execution returns the stored value.
Shouldn't an exception be thrown when the assignment is made and not
when we try to access it?
operator fun setValue(
thisRef: Any?,
property: KProperty<*>, value: String
) {
// assign
// An exception should be generated at this point when the assignment happens
}
Should there be a Compile-time error?
Shouldn't it raise a compile time error when assigning a nullable to a val which is non-null?
Yes, but that's not what's happening here In this case, you are assigning a platform type to your value and telling the compiler to treat it as non-nullable. There should be a runtime error of IllegalStateException-as #MarkoTopolnik points out, this is a bug.
Bug
This bug is covered by KT-8135. Here's a linked issue directly related to delegates, KT-24258.
There's also a discussion topic with some runnable examples here.
When will the runtime exception be thrown?
The error will be triggered when you use proximitySensorin a way that requires a non-nullable value such as the following:
val s: Sensor = proximitySensor
proximitySensor.someMethod()
println(proximitySensor.someProperty)
But this will not throw an exception when the property is initialized! We can fix that...
Why isn't there an error when the Lazy property is first initialized?
Since sensorManager.getDefaultSensor(Sensor.TYPE_PROXIMITY) is implemented in java it returns a platform type. Therefore, the Lazy delegate is of type, Lazy<Sensor!>.
The Lazy class runs the initializer lambda. The result of the lambda is saved and then returned as type T, which is Sensor!.
Solution
If we explicitly declare the type params when calling the lazy function:
private val proximitySensor: Sensor by lazy<Sensor> { sensorManager.getDefaultSensor(Sensor.TYPE_PROXIMITY) }
Then an exception will be thrown as soon as the lazy value is accessed.
Other options
Declare proximitySensor as nullable
The warning is steering you in the right direction either don't check for null or declare it as nullable. Tell the compiler the value should be treated as a nullable by explicitly declaring the type as nullable:
private val proximitySensor: Sensor? by lazy { sensorManager.getDefaultSensor(Sensor.TYPE_PROXIMITY) }
Provide a default value inside the lazy initializer
As you mentioned, sensorManager.getDefaultSensor(Sensor.TYPE_PROXIMITY) may return null. We could return a default value:
val proximitySensor: Sensor by lazy {
sensorManager.getDefaultSensor(Sensor.TYPE_PROXIMITY) ?: SOME_DEFAULT_VALUE
}
A note about Platform Types
Platform types in Kotlin make working with Java more pragmatic by allowing you, the caller, to decide if the type is nullable or non-nullable. The alternative of assuming everything in Java is nullable would mean you would have to do a lot of painful null checking even though the Java code never returns null.
*Note: If you're writing Java code, you can use the #Nullable and #NonNull annotations to provide the metadata to the Kotlin compiler as well as other static analysis tools. Libraries such as the Spring Framework have done this to their APIs.
Here's a talk by Andrey Breslav, a Kotlin developer, that speaks in depth about this design decision of platform types for interoperability.
Thanks to your question this is now a confirmed bug in Kotlin compiler's JVM backend.
Earlier Content of this Answer:
I made an MCVE with your problem:
val sensorManager = SensorManager()
val proximitySensor: Sensor by lazy { sensorManager.sensor }
fun main(args: Array<String>) {
println(proximitySensor)
}
This is the support Java code:
public class SensorManager {
public Sensor getSensor() {
return null;
}
}
public class Sensor {}
And... guess what? It prints null. I call "Kotlin bug" :)
You ask:
Shouldn't an exception be thrown when the assignment is made and not when we try to access it?
In the case of a lazy delegate, the only assignment happens when you access the property for the first time. This propagates to the SynchronizedLazyImpl.value getter, which contains the well-known double-checked lazy init idiom, and it is the only time the initializer block you supply runs.
Even without that detailed discussion it should be clear that the essence of the lazy delegate is to postpone initialization until the latest possible time, which is the first property access. A correct implementation would ensure an exception is thrown before the evaluation of the non-nullable property completes. The current implementation is, unfortunately, not correct.

Only safe or non null assserted calls are allowed on a nullable receiver type of arraylist

Just started using kotlin for android development.My arraylist is declared like this-
var day1: ArrayList<DietPlanDetailModel>? = null
Now I am trying to access an element by its position
val dietPlan= day1[position]
but i am getting below compile time error-
Only safe or non null assserted calls are allowed on a nullable
receiver type of arraylist
Why am i getting this error and how can i resolve it?
The problem is, that you defined the ArrayList as nullable. You have two options here:
don't define the variable nullable (this depends on your code):
var day1: ArrayList<DietPlanDetailModel> = ArrayList()
access your data-structure with a null check:
val dietPlan= day1?.get(position)
As defined, day1 can be null but you're invoking a function by doing [], which is basically the same as calling day1.get(index).
This can throw a NullpointerException, which the Kotlin compiler tries to prevend. Thus, only safe calls like this are allowed: day1?.get().
You told compiler that your variable can be null (and assigned null to it).
day1[position] is essentially day1.get(position) which will crash with NPE if day1 is null -> null.get(position)
If you can guarantee that day1 will be initialized id recommend lateinit or just straight up assigning new Arraylist with declaration. Of course, simple day1?.get(position) works fine.

Categories

Resources