Is there way to make extensions polymorphic?

78 Views Asked by At

I have following java classes I can't change

interface Parent...
class Child1Class implements Parent...
class Child2Class implements Parent...
class GrandChild1Class extends Child1Class ...
...

Now I write Kotlin application.

I want to add some functionality to all the hierarchy.

fun Parent.foo(){ println("Parent.foo")

but I want to have special behaviour for GrandChild1Class

fun GrandChild1Class.foo(){ println("GrandChild1Class.foo")

But if I write:

val grandChild1: Parent = GrandChild1Class()
grandChild1.foo()

I see Parent.foo because of reference type

But I expected to see GrandChild1Class.foo

Is there way to achieve polymorphic behavior for such kinda things ?

1

There are 1 best solutions below

0
Sweeper On BEST ANSWER

No. Kotlin implements extension functions as static Java methods, with the receiver of the extension function being the first parameter of the Java method.

fun String.foo() { }

becomes

public static void foo(String $this$foo) { }

Of course, static methods are statically dispatched, so there is no polymorphism.

The simple workaround is to just check the type with a when.

fun Parent.foo() = when(this) {
    is GrandChild1Class -> println("GrandChild1Class.foo")
    // other cases...
    // remember to put "more specific" cases first!
    else -> println("Parent.foo")

The only instructions in the JVM that can dynamically dispatch methods are invokeinferface, invokevirtual, and invokedynamic. The first two both require the method to be declared in the class/interface of the instance the method is called, which extension methods cannot do. i.e. declaring fun String.foo() in Kotlin can't possibly add a foo method to java.lang.String.

The Kotlin compiler can in theory use invokedynamic. It would find all the applicable extension methods, do some overload resolution, and return a CallSite corresponding to a particular extension method it found. In practice, this would require a lot of designing to figure out what the exact behaviours should be, as well as big changes to the compiler, and breaking source-compatibility.