- Inline casting
In Android development using Kotlin, a very used method is the setOnClickListener
from the View
class, which is defined as fun setOnClickListener(action: (View) -> Unit)
. So if one wants to add a click listener to a Button
(which is a subclass of View
), for example, it’d be:
val button = findViewById<Button>(R.id.btn_id)
button.setOnClickListener { view ->
// argument `view` contains a `View` instance
}
A View
does not contain a setText
method, but a Button
does, so the following is illegal:
button.setOnClickListener { view ->
view.setText = "Clicked" // Unresolved reference: setText
}
To be legal, one would have to do:
button.setOnClickListener { view ->
view as Button
view.setText = "Clicked"
}
So I propose, for readability and for simplicity (since casting more than one lambda argument would increase the LOC), the following syntax:
button.setOnClickListener { (view as Button) ->
view.setText = "Clicked" // valid
}
- Argument inference
Using the same example as above, let’s suppose I have a function that I would like to use to be a button click listener:
fun onClick() {
println("Button clicked")
}
Passing this function in the setOnClickListener
method of the Button
, we’d have:
button.setOnClickListener { view ->
onClick()
// notice that we don't use the `view` lambda argument
}
But if we use the member reference operator (::
), we get an error:
button.setOnClickListener(::onClick)
// Type mismatch: inferred type is KFunction0<Unit> but (View) -> Unit was expected
So I propose the following: if a function receives as argument an object KFunctionN<T>
and an object KFunction0<T>
is passed as argument to this function, this is a valid statement (where N
is the number of arguments and T
is any type). In another words, if a function f
receives a function fA
as argument, and a function fP
contains the same return type as fA
but with no arguments, fP
can be passed as argument to f
.
We can also expand this to other situations, such as:
fun f(fA: (Int, String, Double) -> Int) {
println(fA(1, "2", 3.0))
}
fun fP0(a: Int, b: String): Int = a + b.toInt()
// calling f(::fP0) is legal
fun fP1(a: Int, c: Double): Int = (a * c).toInt()
// calling f(::fP1) is illegal
fun fP2(a: Int, b: String): String = "$a$b"
// calling f(::fP2) is illegal
What do you think?