找不到字段的设置器 - 将 Kotlin 与 Room 数据库结合使用
Posted
技术标签:
【中文标题】找不到字段的设置器 - 将 Kotlin 与 Room 数据库结合使用【英文标题】:Cannot find setter for field - using Kotlin with Room database 【发布时间】:2017-10-28 01:09:42 【问题描述】:我正在与 Room 持久性库集成。我在 Kotlin 中有一个数据类,例如:
@Entity(tableName = "story")
data class Story (
@PrimaryKey val id: Long,
val by: String,
val descendants: Int,
val score: Int,
val time: Long,
val title: String,
val type: String,
val url: String
)
@Entity
和 @PrimaryKey
注释用于 Room 库。当我尝试构建时,它失败并出现错误:
Error:Cannot find setter for field.
Error:Execution failed for task ':app:compileDebugJavaWithJavac'.
> Compilation failed; see the compiler error output for details.
我也尝试过提供默认构造函数:
@Entity(tableName = "story")
data class Story (
@PrimaryKey val id: Long,
val by: String,
val descendants: Int,
val score: Int,
val time: Long,
val title: String,
val type: String,
val url: String
)
constructor() : this(0, "", 0, 0, 0, "", "", "")
但这也不起作用。需要注意的是,如果我将这个 Kotlin 类转换为带有 getter 和 setter 的 Java 类,它就可以工作。任何帮助表示赞赏!
【问题讨论】:
在谷歌示例中的github.com/googlesamples/android-architecture-components/blob/… 中,不可变属性可以正常工作。有人可以分析原因吗?会不会是bug? 【参考方案1】:只需使变量可变,将 val
更改为 var
对于 Kotlin,或者将 private 更改为 public 对于 Java
【讨论】:
【参考方案2】:您可以尝试将 id 变量重命名为另一个名称。它对我有用;
var id: Long? = null
到
var workerId: Long? = null
如果你必须命名为 id 并且你正在使用改造,那么你可能需要添加 SerializedName("id")
【讨论】:
【参考方案3】:如果数据类构造函数中有@Ignore
字段,则需要将其移动到类主体,如下所示:
@Entity(primaryKeys = ["id"])
data class User(
@field:SerializedName("id")
val id: Int,
@field:SerializedName("name")
val name: String,
@field:SerializedName("age")
val age: Int
)
@Ignore
val testme: String?
所有荣誉都转到 GitHub 上的 marianperca:https://github.com/android/architecture-components-samples/issues/421#issuecomment-442763610
【讨论】:
【参考方案4】:如果您的列以 Is 开头,则会引发此错误:
@ColumnInfo(name = "IsHandicapLeague")
@NonNull
var isHandicapLeague: String = "Y"
添加一个默认的set()函数来消除
fun setIsHandicapLeague(flag:String)
isHandicapLeague = flag
【讨论】:
【参考方案5】:由于您的字段标有val
,因此它们实际上是最终的并且没有设置器字段。
尝试将val
替换为var
。
您可能还需要初始化字段。
@Entity(tableName = "story")
data class Story (
@PrimaryKey var id: Long? = null,
var by: String = "",
var descendants: Int = 0,
var score: Int = 0,
var time: Long = 0L,
var title: String = "",
var type: String = "",
var url: String = ""
)
编辑
上述解决方案是对 Kotlin 中此错误的一般修复,当我将 Kotlin 与其他 Java 库(如 Hibernate)一起使用时,我也看到了这一点。如果您想保持 Room 的不变性,请参阅其他一些可能更适合您的情况的答案。
在某些情况下,Java 库的不变性根本无法正常工作,并且在发出可悲的开发人员噪音的同时,不幸的是,您必须将 val
切换为 var
。
【讨论】:
嘿,我觉得这很奇怪,你能看一下吗? ***.com/q/47323883/4620609 感谢您的指点,这个问题对我来说大约需要 2 小时才能解决.....我使用了“private var”并且它抛出了错误,没有任何线索 答案的意思是“不要使用 Room,它会迫使你编写糟糕的代码”,对吧? 请注意,在数据类中使用 var 会导致有效的不变性损失。 这个解决方案对我有用,但对于其他有同样问题的人。我还必须删除我的私有可见性修饰符。【参考方案6】:只需使用 var 而不是 val,如果您使用的是 private 关键字,请将其设为 public。
@Entity(tableName = "story")
data class Story (
@PrimaryKey val id: Long,
var by: String,
var descendants: Int,
var score: Int,
var time: Long,
var title: String,
var type: String,
var url: String
)
【讨论】:
考虑不变性【参考方案7】:我发现此编译错误的另一个原因可能是由于在实体数据类的字段上使用了 Room 的 @Ignore
注释:
@Entity(tableName = "foo")
data class Foo(
// Okay
@PrimaryKey
val id: String,
// Okay
val bar: String,
// Annotation causes compilation error, all fields of data class report
// the "Cannot find setter for field" error when Ignore is present
@Ignore
val causeserror: String
)
使用@Transient
注释时似乎也会发生同样的错误。
我在使用 Room 的 2.2.2
版本时注意到了这个问题:
// build.gradle file
dependencies
...
kapt "androidx.room:room-compiler:2.2.2"
...
希望对某人有所帮助!
【讨论】:
您找到解决方案了吗?卡在同一个案子上。谢谢 就我而言,我需要从实体类的字段中删除@Ignore
。也许您的一个或多个实体类的字段中包含@Ignore
?
显然,Room 库无法实例化您的数据类的对象,因为数据类必须有一个数据库中不存在的字段(因为 @Ignore
注释)。【参考方案8】:
您现在可以使用is
开始您的字段,但您不能在is
旁边有一个数字,例如:is2FooSelected
,您必须重命名为isTwoFooSelected
。
【讨论】:
【参考方案9】:在 Java 中出现此错误。
Java 中不能有以is
或is_
开头的列。
尝试重命名该列。
另一种解决方案:
您要么必须在构造函数中传递字段并使用构造函数参数对其进行初始化,要么为其创建一个 setter。
例子:
public MyEntity(String name, ...)
this.name = name;
...
public void setName(String name)
this.name = name;
【讨论】:
【参考方案10】:如果您希望您的实体可以使用val
不变性,这是可能的。
-
您应该更新到 AndroidX 房间 current version。
检查相关的issue here,它被标记为Won't Fix
现在他们发布了与version 2.0.0-beta01 问题相关的修复程序
现在您可以将不可变的
val
与default value
一起使用,例如:
@Entity("tbl_abc")
data class Abc(
@PrimaryKey
val id: Int = 0,
val isFavourite: Boolean = false
)
之前上面的sn-p会抛出Cannot find setter for field
的错误。更改为 var
是一个很好的解决方法,但我更喜欢实体类不受外部调用的影响
【讨论】:
【参考方案11】:如果有人在 2019 年遇到此线程,则只是一个更新,在花费数小时在线挖掘为什么它应该有效之后,但它没有。
如果您使用的是 AndroidX 版本 (androidx.room:room-<any>:2.*
),则使用 val
可以按预期工作,但在使用旧版 android.arch.persistence.room:<any>:1.1.1
时却不能正常工作,而且似乎 2.*
版本没有在后一个 repo 上发布.
编辑:错别字
【讨论】:
是的,AndroidX 将替换支持库,所以 Room 永远不会是旧的android.arch.persistence.room
上的 2.0。
感谢您的信息
val 在我的情况下对 Int 和 Long 不起作用,即使使用 androidx 和最新的 Room【参考方案12】:
根据https://***.com/a/46753804/2914140,如果你有一个自动生成的主键,你应该这样写:
@Entity(tableName = "story")
data class Story (
val by: String,
val descendants: Int,
val score: Int,
val time: Long,
val title: String,
val type: String,
val url: String
)
@PrimaryKey(autoGenerate = true)
var id: Int = 0
请注意,@PrimaryKey
写在类体内并包含修饰符 var
。
如果您以后想用不同的参数更新数据库中的一行,请使用这些行:
val newStory = story.copy(by = "new author", title = "new title") // Cannot use "id" in object cloning
newStory.id = story.id
dao.update(newStory)
更新
我还是不用AndroidX,Room是'android.arch.persistence.room:runtime:1.1.1'。
您可以从Serializable
扩展此类。但是如果你想从Parcelable
扩展它,你会得到一个警告(超过id
变量):Property would not be serialized inro a 'Parcel'. Add '@IgnoredOnParcel' annotation to remove this warning
:
然后我将 id
从主体移到构造函数。在 Kotlin 中,我使用 @Parcelize
创建 Parcelable
类:
@Parcelize
@Entity(tableName = "story")
data class Story (
@PrimaryKey(autoGenerate = true)
var id: Int = 0,
val by: String,
val descendants: Int,
val score: Int,
val time: Long,
val title: String,
val type: String,
val url: String
) : Parcelable
【讨论】:
如果你这样做,你可能想要覆盖equals
和hashcode
。
@Tenfour04,但为什么呢?它们在data class
中自动生成。但我没有比较两个对象。
它们只为构造函数属性生成。由于您将主键移到构造函数之外,id
不再包含在自动生成的 equals
和 hashcode
中,这将是令人惊讶的行为。
@Tenfour04,同意你的看法,谢谢你的评论。【参考方案13】:
room db 库 java 代码生成存在问题。
我使用的是可选字段isFavorite
。它给了我同样的错误然后我将我的字段名称更改为favorite
然后编译。
之前
var isFavorite: Int? = 0,
改变工作正常后
var favorite: Int? = 0,
谢谢
【讨论】:
更改您的数据类型。我的数据库中有相同的字段名为 isFavourite 我将字段类型更改为布尔值,现在它对我有用。 @ColumnInfo(name = "is_favourite") var isFavourite: Boolean = false【参考方案14】:这是一个错误,已在 Room 2.1.0-alpha01
中修复
https://developer.android.com/jetpack/docs/release-notes#october_8_2018
错误修复
Room 现在将正确使用 Kotlin 的主要构造函数 避免将字段声明为变量的数据类。 b/105769985
【讨论】:
我正在使用2.1.0-alpha03
,但我仍然收到此错误
在 2.1.0-alpha04 中相同【参考方案15】:
嘿,我不知道大家是否知道,但你不能有从is
开始到Room
的列。
例如你不能有这样的
@Entity(tableName = "user")
data class User (
@PrimaryKey var id: Long? = null,
var userName: String = "",
var isConnectedToFB: Boolean = false,
)
【讨论】:
就我而言,“is”前缀不适用于字段名称。但“is”前缀适用于布尔类型字段。 Java 中也会发生同样的事情。您不能使用 "mIs___" 或 "is____" 。它会导致同样的错误。匈牙利符号不会救你。 在 kotlin 中工作 我也用这个答案解决了我的问题。我使用new
作为归档名称。然后将其更改为 newCall,它对我有用。谢谢!
没错。字段名称在布尔类型时不应以“is”开头。【参考方案16】:
另一个原因可能是字段的命名。如果您使用任何预定义的关键字,您将得到相同的错误。 例如,您不能将列命名为“is_active”。
参考:http://www.sqlite.org/lang_keywords.html
【讨论】:
以上是关于找不到字段的设置器 - 将 Kotlin 与 Room 数据库结合使用的主要内容,如果未能解决你的问题,请参考以下文章
Android Room 库错误:找不到字段的设置器。 (科特林)
@Embeddables 的 @ElementCollection 每个都具有 @Embeddable 字段导致“找不到设置器”异常
错误:找不到字段的设置器。 - java.util.ArrayList 中的大小 - Room 中的嵌入式 ArrayList 无法编译