I have a data class
data class Bean {
var value: String = ""
}
I got a Json from server:
{
"value":null
}
What I expect is since value is not optional, so the null cannot be assigned and value will remain as "". But using the debugger I found that value is null, and therefore some method I called on it throws the below exception:
fun matches(stringToMatch: String): Boolean {
return this.value.toLowerCase() == stringToMatch.toLowerCase()
}
kotlin.TypeCastException: null cannot be cast to non-null type java.lang.String
How can I make value remains ""?
You're using a JSON deserialization library (GSON?) that creates your objects by completely sidestepping the initialization you built into them. Most probably it uses a low-level, non-JDK call sun.misc.Unsafe.allocateObject() to allocate a raw data block for your object and then proceeds to populate it from parsed JSON.
The only way to truly fix this is using such a JSON library that understands Kotlin and can replicate its object initialization rules.
BTW your specific JSON example, which explicitly assigns null to a non-null property, should not use the default value, but throw a validation exception.
Related
iam using retrofit 2 with moshi and i have a problem when the optional nested class is broken.
Lets take this as a simple example:
class ClassA(val anyString: String, classB: ClassB?)
class ClassB(val firstString: String, val secondString: String)
I have a class A that has an optional Value from class B.
The Json that is gonna be parsed looks like this:
{
"anystring":"HelloWorld",
"classB":{
"firstString":"IamAFirstString"
//SECOND STRING IS MISSING OR IS NULL
}
}
When i try to parse this json ill get an exception:
com.squareup.moshi.JsonDataException: Non-null value 'secondString' was null at $.classB.secondString
What i want to archive is that i get the ClassA with an null value at classB because that value is optional so its okay when its null. I know that can archive this when i build an adapter and catch the exception but i want to know if theres a general way to archive this behaviour without creating an adapter for every json that i have to parse.
I am running into some unknown error. This breaks my assumption of null-safety with Kotlin data class and Api responses.
Say, I have a data class say Person:
data class Person(val name: String) {
constructor() : this("")
}
This will generate an object Person with default name value i.e. non-null.
Earlier, When I use a default retrofit client with GsonConverterFactory.create() (added as a converter factory). In default mode, Gson doesn't serialize a null value. But today I found out that field is getting serialized to null.
I verfiy the same in ReflectiveTypeAdapterFactory https://github.com/google/gson/blob/master/gson/src/main/java/com/google/gson/internal/bind/ReflectiveTypeAdapterFactory.java#L206
Here the instance value is having non-null field but after reading each field (field.read(in, instance);) it is assigning the null value.
I am expecting the null values to be skipped during serialization or is it deserialization?
Edit:
Looks like it is deserializing nulls not serializing null problem.
Reference: https://github.com/google/gson/issues/1148
Let me know if any detail missing or creating confusion.
You have to make name parameter nullable by changing type;
String
to
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.
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.
I've created classes for each of my data types and use them to read snapshot values whenever possible:
MyClass my = snapshot.getValue(MyClass.class);
Everything is fine until I try to read a particular record:
com.google.firebase.database.DatabaseException: Can't convert object of type java.lang.String to type java.lang.Boolean
Oops. It turns out that another client mistakenly wrote the string literal "true" instead of the boolean value true to a child of this record. Many of my properties including this one are optional. Is it possible through annotations or otherwise for Firebase to still read this object and just ignore properties that are not of the expected type/format, or do I have to deal with this exception and drop the entire record?