13

I am trying to get Room(https://developer.android.com/topic/libraries/architecture/room) work with Kotlin's inline classes as described in Jake Whartons article Inline Classes Make Great Database IDs:

@Entity
data class MyEntity(
    @PrimaryKey val id: ID,
    val title: String
)

inline class ID(val value: String)

When compiling this Room complains that

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).

Looking into the generated Java code I find:

private MyEntity(String id, String title) {
      this.id = id;
      this.title = title;
}

// $FF: synthetic method
public MyEntity(String id, String title, DefaultConstructorMarker $constructor_marker) {
      this(id, title);
}

Mysteriously the default constructor is private now.

When using String as a type for id (or a typealias), the generated Java class constructor looks like expected:

public MyEntity(@NotNull String id, @NotNull String title) {
  Intrinsics.checkParameterIsNotNull(id, "id");
  Intrinsics.checkParameterIsNotNull(title, "title");
  super();
  this.id = id;
  this.title = title;
}

Does somebody now how to keep the default constructor public while using Inline Classes as data entity properties?

4

3 回答 3

0

通过 Lyubomyr Ivanitskiy 的回答和一些修补,它可以完成。

@Entity
class Test(
    @PrimaryKey(autoGenerate = true)
    @get:JvmName("getId")
    @set:JvmName("setId")
    var id: ID,
) {
    constructor(): this(ID(0)) // This is required as replacement for 
    constructor with actual fields
}

当尝试使用 dao 加载此实体时,由于未生成 getter 方法,它将失败。使用内部类 ID 对我不起作用。所以它需要像这样被欺骗:

@Dao
interface TheDao {
    @Deprecated(
        "This is just for the generated Dao_Impl",
        level = DeprecationLevel.WARNING,
        replaceWith = ReplaceWith("getByIdRealId(theID)")
    )
    @Query("select * from test where id = :theID")
    fun getByIdLongType(theID: Long): Test

}

fun TheDao.getByIdRealId(theID: ID): Test = getByIdLongType(theID.id)

这不会阻止使用带有 Long 参数的 getById,但至少会生成一个警告。

测试代码:

@Test
fun createAndLoadTest() {
    val toBeSaved = Test(ID(42))
    dao.save(toBeSaved)
    val fromDB = dao.getByIdRealId(ID(42))
    fromDB shouldNotBe null
    fromDB.id shouldNotBe 42
    fromDB.id shouldBe ID(42)
}
于 2021-09-19T06:36:44.110 回答
0

Kotlin 内联类使用名称修饰。

所以我相信你的房间数据库找不到你的 ID 字段的 getter 和 setter。尝试添加:

...
@get:JvmName("getID")
@set:JvmName("setID")
@PrimaryKey val id: ID,

在您的 ID 参数声明之前禁用修饰。它对我有帮助

于 2021-07-08T11:29:31.097 回答
0

我相信原因是 ID 类将在运行时表示为 String 。所以 $constructor_marker 附加参数是为了保证 MyEntity(String id, String title) 构造函数签名的唯一性,因为这个构造函数可能已经被定义了。但我只是在这里推测。

您能否尝试在 MyEntity 类中显式定义此构造函数并查看它是否有效?

于 2019-10-02T17:06:31.467 回答