Compile the project
Compile without errors
Error: Field "fields" with type "com.myApp.sample" cannot be @Required or @NotNull.
I just updated the Realm version (realm-gradle-plugin) from 3.5.0 to 3.7.2. If I move back to 3.5.0 it works.
class Class(@PrimaryKey var id: Int = 0, var fields: Fields): RealmObject()
Realm version(s): 3.7.2
Realm sync feature enabled: No
Android Studio version: 3.0 Beta 6
Which Android version and device: None
What is Fields? Maybe it should be Fields??
@Zhuinden Why it needs to be optional?
Because if you've previously initialized the schema without @Required annotation, then in your schema, this object is most likely nullable. So you should either migrate it to be not-nullable in your schema, or make it nullable in your type system with ?.
@Zhuinden Could you explain me why this error was not happening on version 3.5.0? What changes were made? Based on this, why the nullity verification needs to be done out of box? Is Realm not able to detect it straightfoward?
Thank you!
3.6.0:
Added Nullable annotation to methods that may return null in order to improve Kotlin usability. This also introduced a dependency to com.google.code.findbugs:jsr305.
org.jetbrains.annotations.NotNull is now an alias for
@Required. This means that the Realm Schema now fully understand Kotlin non-null types.
Actually, Realm is now smarter and doesn't require you to add @Required on top of your non-null Kotlin class variable to make it not-null in the schema.
@Zhuinden So why this is happening? :joy:
Because in Realm 3.5.0
class Class(@PrimaryKey var id: Int = 0, var fields: Fields): RealmObject()
var fields : Fields was automatically treated as nullable even though in the Kotlin type system it was non-null.
In 3.6 we added support for the Kotlin type-system which means that the fields variable is now correctly identified as non-null, but non-null object references are not supported by Realm (as we cannot guarantee that invariant), which is why you are getting that error.
// This would be the accurate class declaration in 3.7.2
class Class(@PrimaryKey var id: Int = 0, var fields: Fields?): RealmObject()
but non-null object references are not supported by Realm (as we cannot guarantee that invariant)
Ahhh, right, object links are always nullable. I should have thought of that.
@ppamorim please let us know if you still need help on this.
Hi folks.
You are breaking the implementation of the library on all possible Kotlin projects just to satisfy a new behavior. I recommend you to think again the implementation and stop forcing the developer to, in a very hard way, change the source code.
Just because Realm is not capable to always return a non null object, it doesn't means that the library can't handle it. Should be good to have a way to disable this nullability verification or add a default value. But let the developer choose what to do with the project, not the inverse.
I will expend a lot of time just to solve this nullability issue and I will increase the risk of failure on my software.
Sorry, but this is very frustating and annoying.
@ppamorim It isn't really a new behavior, but just exposing the reality of the underlying Realm correctly.
If you can guarantee internally that the reference is never null, you can use it as a backing property: https://kotlinlang.org/docs/reference/properties.html#backing-properties and then do the conversion in custom getters and setters, like:
var _person: Person? = null
// Automatically ignored as it doesn't have a backing field
var person: Person
get() = _person!!
set(value) {
_person = value
}
Is it possible to create a nullable field in Kotlin that has a non-null getter, without creating a second field?
Yes, that above code does that:
Slightly different names for the types but you probably get the idea :)
package io.realm.examples.kotlin.model;
import io.realm.RealmObject;
import kotlin.Metadata;
import kotlin.jvm.internal.Intrinsics;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@Metadata(
mv = {1, 1, 6},
bv = {1, 0, 1},
k = 1,
d1 = {"\u0000\u0014\n\u0002\u0018\u0002\n\u0002\u0018\u0002\n\u0002\b\u0002\n\u0002\u0018\u0002\n\u0002\b\t\b\u0016\u0018\u00002\u00020\u0001B\u0005垄\u0006\u0002\u0010\u0002R\u001c\u0010\u0003\u001a\u0004\u0018\u00010\u0004X\u0086\u000e垄\u0006\u000e\n\u0000\u001a\u0004\b\u0005\u0010\u0006\"\u0004\b\u0007\u0010\bR$\u0010\n\u001a\u00020\u00042\u0006\u0010\t\u001a\u00020\u00048F@FX\u0086\u000e垄\u0006\f\u001a\u0004\b\u000b\u0010\u0006\"\u0004\b\f\u0010\b篓\u0006\r"},
d2 = {"Lio/realm/examples/kotlin/model/Dog;", "Lio/realm/RealmObject;", "()V", "_cat", "Lio/realm/examples/kotlin/model/Cat;", "get_cat", "()Lio/realm/examples/kotlin/model/Cat;", "set_cat", "(Lio/realm/examples/kotlin/model/Cat;)V", "value", "cat", "getCat", "setCat", "production sources for module kotlinExample"}
)
public class Dog extends RealmObject {
@Nullable
private Cat _cat;
@Nullable
public final Cat get_cat() {
return this._cat;
}
public final void set_cat(@Nullable Cat var1) {
this._cat = var1;
}
@NotNull
public final Cat getCat() {
Cat var10000 = this._cat;
if(this._cat == null) {
Intrinsics.throwNpe();
}
return var10000;
}
public final void setCat(@NotNull Cat value) {
Intrinsics.checkParameterIsNotNull(value, "value");
this._cat = value;
}
}
@cmelchior I am doing something similar, but just renaming the variable to avoid any Realm migration. Something like this:
open class Foo(@PrimaryKey var id: Int = 0, var bar: Bar?): RealmObject() {
@Ignore
var unwrappedBar: Bar = bar!!
private set
}
@cmelchior
var _person: Person? = null
// Automatically ignored as it doesn't have a backing field
var person: Person
get() = _person!!
set(value) {
_person = value
}
This in Kotlin will fail to compile, var _person: Person? = null requires to have a getter or setter. Try it yourself using Kotlin 1.1.51 and you'll get a lint error.
Let's face the truth, Realm is in any way Kotlin friendly, would that be the case then Data Classes which are the proper way of defining entities in Kotlin will work out of the box instead of getting the classical "must declare a public constructor" error at compile-time.
@mradzinski considering Realm creates a proxy class with which it replaces the object's getters/setters with the proxy's accessors, and the objects also mutate over time (Realm change), I don't think they are suitable to be data classes?
The classical must declare a public constructor is the same as if you didn't have a no-arg constructor on a Fragment. It is a user error from the library perspective, imo.
I'm still not an official Realm person.
Data classes are in the same category as AutoValue classes, and while they could have a place in a Realm world, being the model class isn't one of them. You can read more in #5426
@cmelchior If I get this right, what you have done destroys a very nice feature of Kotlin: lateinit variables. And will force us to scatter null checks all over the place.
Plus, I now have to correct 23 compilation errors (and I'm lucky, could be a lot more!)
@nkanellopoulos
Added realm.ignoreKotlinNullability as a kapt argument to disable treating kotlin non-null types as @Required (#5412) (introduced in v3.6.0.)
If your object has an Id, you can treat it as foreign key and make it required. for example
public class Category extends RealmObject {
@PrimaryKey
private long id;
}
public class Item extends RealmObject {
@PrimaryKey
private long id;
private long categoryId; // primitive types cant be null
public getCategory(Realm realm) {
return realm.where(Category.class).equalTo("id", categoryId).findFirst();
}
}
Most helpful comment
Hi folks.
You are breaking the implementation of the library on all possible
Kotlinprojects just to satisfy a new behavior. I recommend you to think again the implementation and stop forcing the developer to, in a very hard way, change the source code.Just because
Realmis not capable to always return a non null object, it doesn't means that the library can't handle it. Should be good to have a way to disable this nullability verification or add a default value. But let the developer choose what to do with the project, not the inverse.I will expend a lot of time just to solve this nullability issue and I will increase the risk of failure on my software.
Sorry, but this is very frustating and annoying.