- 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?