【问题标题】:Generated setter clashes with an interface method生成的 setter 与接口方法冲突
【发布时间】:2019-03-13 17:46:54
【问题描述】:

我正在编写一个实现接口的类,该接口公开setSelected 方法。这个类将有一个selected 属性:

private class Foo : IFoo {
        var selected = false

        override fun setSelected(isActive: Boolean) {
            selected = isActive
        }

    }

但是编译器抱怨说,因为 Kotlin 为 selected 生成了一个 setter,这两种方法会发生冲突:

Error:(14, 9) Kotlin: [com.bar.jvmTest] Platform declaration clash: The following declarations have the same JVM signature (setSelected(Z)V):
    fun <set-selected>(<set-?>: Boolean): Unit defined in foo.bar.baz.Foo
    fun setSelected(isActive: Boolean): Unit defined in foo.bar.baz.Foo
Error:(24, 9) Kotlin: [com.bar.jvmTest] Platform declaration clash: The following declarations have the same JVM signature (setSelected(Z)V):
    fun <set-selected>(<set-?>: Boolean): Unit defined in foo.bar.baz.Foo
    fun setSelected(isActive: Boolean): Unit defined in foo.bar.baz.Foo

  • 我很想删除自定义方法以利用 setter,但是它没有标记为override,因此我的类没有完全实现接口:
    Error:(11, 13) Kotlin: [com.bar.jvmTest] Class 'Foo' is not abstract and does not implement abstract member public abstract fun setSelected(isActive: Boolean): Unit defined in bar.baz
    

  • 我知道我可以将selected 重命名为例如dataSelected 以便生成的 setter 不会与方法冲突,但应该有办法保持这个简单的属性名称 按预期实现接口。
  • 有没有办法让 Kotlin 编译器不为此属性生成 setter,或者将其标记为 override

    【问题讨论】:

      标签: kotlin properties getter-setter generated-code


      【解决方案1】:

      您可以创建一个没有支持字段的属性,然后像这样覆盖您的抽象函数:

      class Foo : IFoo {
        private var hiddenSelected = false
      
        val selected get() = hiddenSelected
      
        override fun setSelected(isActive: Boolean) {
          hiddenSelected = isActive
        }
      }
      

      更新:

      睡了之后,我觉得这个解决方案一点都不好,原因有两个:

      1. 它引入了一个不必要的新字段 (hiddenSelected)
      2. 您不能使用标准 Kotlin 方式(= 运算符)对该字段进行赋值

      我认为最好的解决方案是:

      class Foo : IFoo {
        @set:JvmName("setSelected0")
        var selected: Boolean = false
          set(value) { setSelected(value) }
      
        override fun setSelected(isActive: Boolean) {
          // Possibly some other stuff
          println("Now i'm using my own setter!")
          selected = isActive
        }
      }
      

      使用注解@JvmName,您可以告诉编译器如何命名该特定函数。 Kotlin 会自动为每个属性创建一个 getter 和 setter,因此您需要使用 set: 修饰符来注释该属性的 setter 而不是属性本身。

      此外,为该属性实现自定义设置器也非常重要,这样您就可以安全地这样编写:

      Foo().selected = true // This also prints "Now i'm using my own setter!"
      

      而不是这个:

      Foo().setSelected(true)
      

      您的 setter 可能会执行一些其他操作(例如打印该日志),这可能会产生副作用,因此您需要确保调用正确的 setter。这有时会有点棘手,因为 Kotlin 总是为每个可变变量创建一个 setter (var)。

      【讨论】:

        【解决方案2】:

        你可以use @JvmName to rename the generated setter:

        private class Foo : IFoo {
            @set:JvmName("setSelected0")
            var selected = false
        
            override fun setSelected(isActive: Boolean) {
                selected = isActive
            }
        }
        

        在 Kotlin 中的使用保持不变。

        【讨论】:

        • 我在这里看到的问题是,对于同一个 var,现在有 2 个单独的设置器,它们可以有不同的实现。
        • 您可以将 setter 设为私有,但 Kotlin 客户端必须调用 setSelected 而不是 selected =。或者标记 setSelected 已弃用,因此它只能通过接口使用,但这对 Java 客户端不利,如果有的话。
        猜你喜欢
        • 2015-12-04
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2014-01-30
        • 1970-01-01
        相关资源
        最近更新 更多