Martin Devillers
05/21/2019, 12:52 PMMartin Devillers
05/21/2019, 12:53 PMMartin Devillers
05/21/2019, 12:54 PMf
that gets called is the one in A
, which is counterintuitive, I would expect the one in B
.Martin Devillers
05/21/2019, 12:54 PMmarstran
05/21/2019, 1:05 PMoverride
modifier). They're just overloads. I'm guessing the f
in B
gets name-mangled or something during compilation so it works after type erasure.Martin Devillers
05/21/2019, 1:44 PMoverride
, the method binding happens at compile-time, and it’s the compiler which chooses to bind to the method in A
rather than the one in B
. It’s just strange that it chooses to do so.Ilmir Usmanov [JB]
05/21/2019, 3:16 PMdsavvinov
05/21/2019, 3:19 PMA.f
is more specific than B.f
, because they differ only in parameter of accepted lambda, which is a contravariant position.
In other words, A.f
accepts only lambdas which operate on A
, while B.f
accepts lambdas which operate both on A
and B
.
This similar to how we choose f(Int)
over f(Any)
in f(42)
, but lambda makes it much more confusing (and, honestly, I also thought that something is wrong here at first 😅 )Martin Devillers
05/21/2019, 3:45 PMclass A
twice? In practice, it doesn’t, it prints class A
then class B
.
open class A
class B: A()
fun f(block: (A) -> Unit) = A()
fun f(block: (B) -> Unit) = B()
fun main() {
val blockA: (A) -> Unit = {}
val blockB: (B) -> Unit = {}
println(f(blockA)::class)
println(f(blockB)::class)
}
dsavvinov
05/21/2019, 3:49 PMf(block: (A) -> Unit)
can not accept blockB
— again, theoretically speaking, this is because parameters are contravariant. And speaking more practically, body of blockB
treats parameter as B
, while f(block: (A) -> Unit)
may pass here something which is not B
(e.g., instance of A
)Martin Devillers
05/21/2019, 3:56 PMMartin Devillers
05/21/2019, 3:57 PM