5

To get a OneToMany relation with Room I create a POJO with @Embedded object and @Relation variable.

data class SubjectView(
    @Embedded
    var subject: Subject,

    @Relation(parentColumn = "idWeb", entityColumn = "subject_id", entity = Topic::class)
    var topics: List<Topic>?
)

But while compiling I have this error

 error: Entities and Pojos must have a usable public constructor. You can have an empty constructor or a constructor whose parameters match the fields (by name and type)
[...]
Tried the following constructors but they failed to match:
SubjectView(biz.eventually.atpl.data.db.Subject,java.util.List<biz.eventually.atpl.data.db.Topic>) : [subject : subject, topics : null]

Well, that constructor [subject : subject, topics : null] looks like the good one ???

However, if I change my class with no-arg constructor and an all params constructor, it does work.

class SubjectView() {
    @Embedded
    var subject: Subject = Subject(-1, -1, "")

    @Relation(parentColumn = "idWeb", entityColumn = "subject_id", entity = Topic::class)
    var topics: List<Topic>? = null

    constructor(subject: Subject, topics: List<Topic>?) : this() {
        this.subject = subject
        this.topics = topics
    }
}

I would like to know why the first (quicker) version does not compile, as it is not as the documentation shows.

Default args for all variables (as I could have seen on other post) in a constructor (data) class seems not to be mandatory though?

Thanks

Tweety B
  • 65
  • 6

1 Answers1

5

There are several topics how data class generate the constructors.

Since you have a nullable Object inside your constructor, it will generate all possible constructors. That means it generates

constructor(var subject: Subject)
constructor(var subject: Subject, var topics: List<Topic>) 

There are two ways to solve that. The first one is to predefine all values like and create another ignored constructor with the desired constructor.

data class SubjectView(
    @Embedded
    var subject: Subject,

    @Relation(parentColumn = "idWeb", entityColumn = "subject_id", entity = Topic::class)
    var topics: List<Topic> = ArrayList()
) {
 @Ignore constructor(var subject: Subject) : this(subject, ArrayList())
}

Another way is creating a half-filled data class like

data class SubjectView(@Embedded var subject: Subject) {
    @Relation var topics: List<Topic> = ArrayList()
}

Take care that the first solution is the proper solution and you need to set @Ignore to any other constructor.

Emanuel
  • 8,027
  • 2
  • 37
  • 56
  • Hi! I don't understand your answer because you make `topics` non null. Or we want `topics` to be nullable... – Blunderer Nov 30 '17 at 21:02
  • Even if you want to be nullable you should create an instance of Topic and validate if the entries are "empty", not null. – Emanuel Nov 30 '17 at 21:04
  • Or add the @JvmOverloads annotation to the main class constructor like `data class SubjectView @JvmOverloads constructor(...)` – Afzal N Aug 31 '18 at 22:03
  • first mistake beginners do.. using jvmoverloads. Try to avoid them if you dont need them. There are many cons why using JvmOverloads is bad. (Compile time, speed, generated code, ..) – Emanuel Sep 01 '18 at 09:04
  • I'm slightly confused at your initial statement. From my understanding, multiple constructors are only generated when you use **default** arguments, not just when you use nullable arguments. – kassim Nov 14 '18 at 11:24