Сгенерированные конфликты сеттера с интерфейсным методом - PullRequest
3 голосов
/ 13 марта 2019

Я пишу класс, реализующий интерфейс, который предоставляет метод 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?

Ответы [ 2 ]

1 голос
/ 14 марта 2019

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

private class Foo : IFoo {
    @set:JvmName("setSelected0")
    var selected = false

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

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

1 голос
/ 13 марта 2019

Вы можете создать свойство без вспомогательного поля, а затем переопределить свою абстрактную функцию следующим образом:

class Foo : IFoo {
  private var hiddenSelected = false

  val selected get() = hiddenSelected

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

Обновление:

После сна я думаю, что это решение совсем не так хорошо по двум причинам:

  1. Вводит новое поле (hiddenSelected), в котором нет необходимости
  2. Вы не можете делать присвоения этому полю, используя стандартные способы Котлина (оператор =)

Я думаю, что лучшим решением будет следующее:

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...