Как работает синтаксис доступа к свойствам Kotlin для классов Java (т.е. EditText setText)?

Я пытаюсь переключить свой проект Android на Kotlin. У меня есть EditText (подкласс TextView), для которого я хочу установить подсказку и текст программно. Подсказка работает как положено. Для текста, однако, я получаю исключение несоответствия типов, если я пытаюсь сделать это, используя синтаксис Kotlin:

    val test = EditText(context)

    test.setHint("hint")    // Lint message: "Use property access syntax"
    test.hint = "hint"      // ok

    test.setText("text")    // ok (no lint message)
    test.text = "text"      // Type mismatch: inferred type is kotlin.String but android.text.Editable! was expected

Если мы посмотрим на объявление, мы найдем идентичные подписи, унаследованные от TextView:

    public final void setHint(CharSequence hint)

    public final void setText(CharSequence text)

У меня сложилось впечатление, что xy = z было сокращением для x.setY(z) но, очевидно, это впечатление было неправильным. setText() рассматривается как обычный метод, а не как установщик, но какая разница между этими двумя методами, которая заставляет компилятор вести себя по-разному? Единственное, о чем я могу подумать, это то, что TextView имеет свойство mHint но я не думаю, что это может иметь место.

Еще одна вещь, которую я не совсем понимаю: откуда взялся android.text.Editable? Нет соответствующего метода setText(Editable) и нет открытого поля этого типа.

Ответы

Ответ 1

При генерации синтетического свойства пары Java getter/setter Kotlin сначала ищет геттер. Геттера достаточно, чтобы создать синтетическое свойство с типом геттера. С другой стороны, свойство не будет создано, если присутствует только сеттер.

При запуске сеттера создание свойства становится более сложным. Причина в том, что геттер и сеттер могут иметь различный тип. Более того, геттер и/или сеттер могут быть переопределены в подклассе.

В вашем случае класс TextView содержит getter CharSequence getText() и setter void setText(CharSequence). Если у вас есть переменная типа TextView, ваш код будет работать нормально. Но у вас есть переменная типа EditText. И класс EditText содержит переопределенный getter Editable getText(), что означает, что вы можете получить Editable для EditText и установить Editable в EditText. Поэтому Котлин разумно создает синтетическое свойство text типа Editable. Класс String не является Editable, поэтому вы не можете назначить экземпляр String для свойства text класса EditText.

Ответ 2

Чтобы избежать несоответствия типов, вы можете использовать внутренний класс Factory класса Editable. Итак, теперь вы можете сделать что-то вроде:

textview.text = Editable.Factory.getInstance().newEditable("your text")  

Ответ 3

В качестве альтернативы вы можете написать расширение:

fun String.toEditable(): Editable =  Editable.Factory.getInstance().newEditable(this)

Затем вы можете использовать его как таковой:

mEditText.text = myString.toEditable()

Ответ 4

android.text.Editable происходит от getText(). Мне кажется, что разрешение obj.text = value в Котлине - двухэтапный процесс.

  • Компилятор пытается найти свойство text или метод Java getText, из которого он выводит тип свойства
  • Для свойства inferred type компилятор пытается найти соответствующий набор свойств или метод Java setText(PropertyType value)

Так как в 1. выведенном типе Editable ошибка editText.text = "value" не выполняется с ошибкой Type mismatch.