'How to check if a class has overriden a default method from an interface using Reflection in Kotlin or Java?

I have an interface with a default method, and two classes which implement this interface. One of the classes overrides the default method, and the other does not.

interface MyType {
  fun giveHello(): String = "Hello!"
}

class Polite: MyType {
  // Does not override giveHello()
}

class Rude: MyType {
  override fun giveHello(): String = "I don't like you"
}

I get access to the giveHello method using reflection like this:

val methodOfPolite = Polite::class.java.getDeclaredMethod("giveHello")
val methodOfRude = Rude::class.java.getDeclaredMethod("giveHello")

There's one weird thing here. The polite class does not override the giveHello method, but the declaringClass of this method object still points to Polite.

So is there a way I can check whether the class actually did override the default interface method or not?

My use case looks something like this (assuming we can get the behaviour I'm asking for in a property called isOverriden):

if (methodOfPolite.isOverriden) {
  // do something
} else {
  // do something else
}


Solution 1:[1]

As described in KT-4779, currently Kotlin default functions are not implemented using actual Java/JVM default methods. The default implementation lives in a static method instead, and all classes using that default implementation just call that static method. This is done to ensure Kotlin default functions also work on the 1.6 JVM target which doesn't have them yet.

So your code roughly compiles to this Java equivalent:

public interface MyType {
  public String giveHello();

  public static class MyTypeImpls {
     public static String giveHello() { return "Hello!" }
  }
}

public final class Polite implements MyType {
  //does not override
  public String giveHello() { return MyType.MyTypeImpls.giveHello() }
}

public final class Rude implements MyType {
  //does override
  override fun giveHello() { return "I don't like you" }
}

This is why java reflection thinks both classes override the function, ie because they actually do.

You need to use Kotlin reflection here, notably declaredMemberFunctions and memberFunctions:

fun overridesGiveHello<T: MyType>(cls: KClass<T>) =
        cls.memberFunctions.first { it.name == "giveHello" } in cls.declaredFunctions

println(overridesGiveHello(Polite::class)) //false
println(overridesGiveHello(Rude::class))  //true

Solution 2:[2]

In this specific case I think the isDefault() method should return true.

I would have expected Polite::class.java.getMethod("giveHello") to return the method, but not getDeclaredMethod(), but we are in the world of using Java reflection on Kotlin classes and interfaces. The Java expectations may not be met.

You could use kotlin reflection though, use declaredMembers to get the properties and functions from the KClass. Note, import from kotlin.reflect.full is required, since extension methods are used.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1
Solution 2