kotlinpropertiesgetter-settergenerated-code

Generated setter clashes with an interface method


I am writing a class implementing an interface that exposes a setSelected method. This class will have a selected property:

private class Foo : IFoo {
        var selected = false

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

    }

However the compiler complains, as Kotlin generates a setter for selected, that the two methods clash:

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

  • I'd love to remove the custom method to leverage the setter, but then it is not marked override and so my class does not fully implement the interface:

    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
    

  • I know I can rename selected to e.g. dataSelected so that the generated setter doesn't clash with the method, but there should be a way to keep this simple property name and implement the interface as expected.
  • Is there a way to ask the Kotlin compiler to not generate a setter for this property, or to have it marked as override?


    Solution

  • You can create a property without a backing field, and then override your abstract function like this:

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

    Update:

    After sleeping on it, I think this solution is not so good at all because of 2 reasons:

    1. It introduces a new field (hiddenSelected) which is not necessary
    2. You cannot do assignments to that field using the standard Kotlin ways (= operator)

    I think the best solution would be this:

    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
      }
    }
    

    Using the annotation @JvmName you can tell the compiler how to name that specific function. Kotlin automatically creates a getter and setter for every property, so you need to use the set: modifier to annotate the setter of that property and not the property itself.

    Also it is very important that you implement a custom setter for that property so you can safely write this:

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

    instead of this:

    Foo().setSelected(true)
    

    Your setter can possibly do some other stuff (like printing that log), which can have side effects, so you need to make sure that you call the right setter. This can be sometimes a bit tricky since Kotlin always creates a setter for every mutable variable (var).