Сгенерированный установщик конфликтует с методом интерфейса

#kotlin #properties #getter-setter #generated-code

#kotlin #свойства #получатель-установщик #сгенерированный код

Вопрос:

Я пишу класс, реализующий интерфейс, который предоставляет setSelected метод. У этого класса будет selected свойство:

 private class Foo : IFoo {
        var selected = false

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

    }
  

Однако компилятор жалуется, поскольку Kotlin генерирует установщик для selected , что два метода конфликтуют:

 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
  

  • Я бы хотел удалить пользовательский метод, чтобы использовать установщик, но тогда он не отмечен 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 , чтобы сгенерированный установщик не конфликтовал с методом, но должен быть способ сохранить это простое имя свойства и реализовать интерфейс, как ожидалось.
  • Есть ли способ попросить компилятор Kotlin не генерировать установщик для этого свойства или пометить его как override ?

    Ответ №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 автоматически создает средство получения и установки для каждого свойства, поэтому вам нужно использовать set: модификатор для аннотирования средства установки этого свойства, а не самого свойства.

    Также очень важно, чтобы вы внедрили пользовательский установщик для этого свойства, чтобы вы могли безопасно написать это:

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

    вместо этого:

     Foo().setSelected(true)
      

    Возможно, ваш установщик может выполнять некоторые другие действия (например, печатать этот журнал), что может иметь побочные эффекты, поэтому вам нужно убедиться, что вы вызываете правильный установщик. Иногда это может быть немного сложно, поскольку Kotlin всегда создает установщик для каждой изменяемой переменной ( var ).

    Ответ №2:

    Вы можете использовать @JvmName для переименования сгенерированного установщика:

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

    Использование из Kotlin остается прежним.

    Комментарии:

    1. Проблема, которую я вижу здесь, заключается в том, что теперь есть 2 отдельных установщика для одного и того же var , которые могут иметь разные реализации.

    2. Вы могли бы сделать установщик закрытым, но тогда клиенты Kotlin должны вызывать setSelected вместо selected = . Или отметьте setSelected устаревшим, чтобы он использовался только через интерфейс, но это плохо для клиентов Java, если они у вас есть.