Room编译后持久化疑问:为何两个@Entity数据类表现不同?
为啥一个用
val的Room实体类报错找不到setter,另一个却正常? 嘿,这个问题其实是Room处理Kotlin数据类的规则差异导致的,咱们拆解来看:
先搞懂Room对val数据类的处理逻辑
Room完全支持用val定义实体类字段——前提是这个数据类的主构造函数里只包含需要Room持久化的字段(没有@Ignore标记的)。因为Kotlin数据类会自动给val生成只读getter,Room可以直接通过主构造函数来实例化对象,根本不需要setter。
你的Name类为啥出问题?
你的Name类里有个被@Ignore标记的englishNameMeaning,而且这个字段还放在了主构造函数里。这就麻烦了:
Room生成代码的时候,会去匹配主构造函数的参数和它需要持久化的列,但englishNameMeaning是被忽略的,Room不会管它。这就导致Room找不到一个「只包含持久化字段」的构造函数来创建Name实例——毕竟主构造里混了一个Room不认的字段,而你一开始用的是val(没有setter),Room没法通过空构造+setter的方式初始化,自然就报错找不到setter了。
你改成var再加空构造后,Room就可以先通过空构造创建对象,再调用var的setter给各个字段赋值,问题自然解决。
那Aya类为啥没事?
再看Aya类:
- 所有
val字段都没有@Ignore,全是Room要持久化的列 - 主构造函数的参数正好对应所有持久化字段,没有额外的「无关」参数
- Room可以直接把从数据库查出来的值,一股脑塞进主构造函数里实例化
Aya对象,完全不需要依赖setter,所以用val也一点问题都没有。
额外提个优化方案(不用改val也能解决)
其实你没必要把Name的val改成var,只要给它加一个不含被忽略字段的构造函数就行,比如:
@Entity(tableName = "names_of_creator") data class Name( @SerializedName("name") @ColumnInfo(name = "arabic_name") val arabicName: String, @SerializedName("transliteration") @ColumnInfo(name = "transliteration") val transliteration: String, @SerializedName("number") @PrimaryKey @ColumnInfo(name = "id") val number: Int, @ColumnInfo(name = "name_meaning") val meaning: String, // 把@Ignore字段移到主构造最后,方便构造函数重载 @SerializedName("en") @Ignore val englishNameMeaning: EnglishNameMeaning? ){ // 专门给Room用的构造函数,只包含需要持久化的字段 constructor(arabicName: String, transliteration: String, number: Int, meaning: String) : this(arabicName, transliteration, number, meaning, null) }
这样Room就能识别这个构造函数,依然可以用val保持字段的不可变性,更符合Kotlin的设计习惯。
内容的提问来源于stack exchange,提问作者apksherlock
相关产品推荐
相关产品推荐

