I'm trying to switch my Android project to Kotlin. I have an EditText
(a subclass of TextView
) for which I want to set a hint and text programmatically. The hint works as expected. For text, though, I'm getting a type mismatch exception if I try to do it using Kotlin setter syntax:
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
If we look at the declaration, we'll find identical signatures inherited from TextView
:
public final void setHint(CharSequence hint)
public final void setText(CharSequence text)
I had an impression that x.y = z
was a shortcut for x.setY(z)
but apparently that impression was wrong. setText()
is treated as a normal method rather than a setter, but what's the difference between these two methods that makes the compiler behave differently? The only one I can think of is that TextView
has an mHint
property but I don't think it might be the case.
Another thing I don't quite understand is, where does android.text.Editable
come from? There is no corresponding setText(Editable)
method, nor is there a public field of this type.
Thanks.
When generating a synthetic property for a Java getter/setter pair Kotlin first looks for a getter. The getter is enough to create a synthetic property with a type of the getter. On the other hand the property will not be created if only a setter presents.
When a setter comes into play property creation becomes more difficult. The reason is that the getter and the setter may have different type. Moreover, the getter and/or the setter may be overridden in a subclass.
In your case the TextView
class contains a getter CharSequence getText()
and a setter void setText(CharSequence)
. If you had a variable of type TextView
your code would work fine. But you have a variable of type EditText
. And the EditText
class contains an overridden getter Editable getText()
, which means that you can get an Editable
for an EditText
and set an Editable
to an EditText
. Therefore, Kotlin reasonably creates a synthetic property text
of type Editable
. The String
class is not Editable
, that's why you cannot assign a String
instance to the text
property of the EditText
class.
To avoid type mismatch, you can use the Factory inner class of Editable class.
So you can do now something like:
textview.text = Editable.Factory.getInstance().newEditable("your text")
The android.text.Editable
comes from the getText()
. It appears to me that the obj.text = value
resolution in Kotlin is 2 step process.
- Compiler tries to finds a
text
property or Java method getText
from which it infers the property type
- For the inferred property type the compiler tries to find corresponding property setter or Java method
setText(PropertyType value)
Since in the 1. the inferred type is Editable
the editText.text = "value"
fails with Type mismatch
error.
Alternatively you could write an extension:
fun String.toEditable(): Editable = Editable.Factory.getInstance().newEditable(this)
You can then use it as such:
mEditText.text = myString.toEditable()