【问题标题】:Kotlin delegates with Room persistence lib带有 Room 持久性库的 Kotlin 代表
【发布时间】:2017-11-14 09:09:54
【问题描述】:

我目前正在使用 Kotlin 开发一个新的 android 应用程序。我尝试实现 Room 用于数据存储,但我没有让它与 Kotlin 代表一起使用。

我创建了一个Identifier 委托,以确保在初始化后不会更改 id。委托看起来像这样:

class Identifier: ReadWriteProperty<Any?, Long> {

    private var currentValue = -1L

    override fun getValue(thisRef: Any?, property: KProperty<*>): Long {
        if (currentValue == -1L) throw IllegalStateException("${property.name} is not initialized.")
        return currentValue
    }

    override fun setValue(thisRef: Any?, property KProperty<*>, value: Long) {
        if (currentValue != -1L) throw IllegalStateException("${property.name} can not be changed.")
        currentValue = value
    }
}

我的实体类如下所示:

@Entity
class Sample {

    @delegate:PrimaryKey(autoGenerate = true)
    var id by Identifier()
}

当我尝试启动应用程序时,kapt 给了我以下错误消息:

Cannot figure out how to save this field into database. You can consider adding a type converter for it.
private final com.myapp.persistence.delegates.Identifier id$delegate = null;

我可以在不为每个代表写TypeConverter 的情况下以某种方式使其工作吗?

【问题讨论】:

    标签: android delegates kotlin android-room


    【解决方案1】:

    使用@delegate:Ignore

    我的实体对象和... by lazy 属性也有类似问题。

    例如:

    var name: String = "Alice"
    
    val greeting: String by lazy { "Hi $name" }
    

    这里的问题是 Room“无法弄清楚如何将此字段保存到数据库中”。我尝试添加“@Ignore”,但收到一条 lint 消息“此注释不适用于目标‘带委托的成员属性’。”

    事实证明,这种情况下正确的注解是@delegate:Ignore

    解决方案:

    var name: String = "Alice"
    
    @delegate:Ignore
    val greeting: String by lazy { "Hi $name" }
    

    【讨论】:

    • 谢谢,成功了。您如何确定需要使用@delegate:Ignore 而不是@Ignore?
    【解决方案2】:

    不幸的是,没有 - Room 默认为实体中定义的每个字段创建一个列,当我们使用 delegate 时,我们会生成如下代码:

       @PrimaryKey(autoGenerate = true)
       @NotNull
       private final Identifier id$delegate = new Identifier();
    
       public final long getId() {
          return this.id$delegate.getValue(this, $$delegatedProperties[0]);
       }
    
       public final void setId(long var1) {
          this.id$delegate.setValue(this, $$delegatedProperties[0], var1);
       }
    

    这就是 Room 尝试为 Identifier id$delegate 创建列的原因。

    但是,如果您只是想确保 id 在对象初始化后不会更改,则根本不需要委托,只需将变量标记为 final 并将其放在构造函数中,例如:

    @Entity
    data class Sample(
        @PrimaryKey(autoGenerate = true)
        val id: Long
    )
    

    【讨论】:

      【解决方案3】:

      以下代码有类似问题:

      data class ForecastItem(
      val city: String,
      val time: Long,
      val temp: Int,
      val tempMax: Int,
      val tempMin: Int,
      val icon: String
      ) {
          val formattedTime: String by lazy {
              val date = Date(this.time * 1000L)
              val dateFormat = SimpleDateFormat("E HH:mm")
              dateFormat.timeZone = TimeZone.getTimeZone("GMT+1")
              dateFormat.format(date)
        }
      }
      

      在这种情况下,由于使用 formattedTime 的委托,我得到了同样的错误:

      无法弄清楚如何将此字段保存到数据库中。你可以考虑为它添加一个类型转换器。

      就我而言,我最终用一个函数替换了委托。它不一样,但它对我有用。我不确定这是否真的是设计解决方案的最佳方式,但我希望它可以帮助遇到类似问题的任何人。

      fun getFormattedTime(): String {
          val date = Date(this.time * 1000L)
          val dateFormat = SimpleDateFormat("E HH:mm")
          dateFormat.timeZone = TimeZone.getTimeZone("GMT+1")
          return dateFormat.format(date)
      }
      

      【讨论】:

        猜你喜欢
        • 2019-08-09
        • 2019-04-25
        • 2018-11-11
        • 2020-04-07
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多