Here is my solution, it's written in Kotlin (JVM language).
//See: http://www.tutorialspoint.com/java/java_overriding.htm
inline fun Method.isOverridableIn(cls: Class<*>): Boolean {
if (!isOverridable) return false
if (!isSubclassVisible) return false
if (!declaringClass.isAssignableFrom(cls)) return false
if (isPublic) return true
if (isPackageVisible && cls.getPackage() == declaringClass.getPackage()) return true
return false
}
private fun Method.areParametersCovariant(other: Method): Boolean {
if (getParameterTypes() == null && other.getParameterTypes() == null) return true
if (getParameterTypes() == null || other.getParameterTypes() == null) return false
val myPrmTypes = getParameterTypes()!!
val otherPrmTypes = other.getParameterTypes()!!
if (myPrmTypes.size != otherPrmTypes.size) return false
for (i in myPrmTypes.indices)
if (!(otherPrmTypes[i].isAssignableFrom(myPrmTypes[i]))) return false
return true
}
private fun Method.areParametersTheSameAs(other: Method): Boolean {
if (getParameterTypes() == null && other.getParameterTypes() == null) return true
if (getParameterTypes() == null || other.getParameterTypes() == null) return false
val myPrmTypes = getParameterTypes()!!
val otherPrmTypes = other.getParameterTypes()!!
if (myPrmTypes.size != otherPrmTypes.size) return false
for (i in myPrmTypes.indices)
if (otherPrmTypes[i] != myPrmTypes[i]) return false
return true
}
private fun Method.isReturnTypeCovariant(other: Method): Boolean {
if (getReturnType() == null && other.getReturnType() == null) return true
if (getReturnType() == null || other.getReturnType() == null) return false
return other.getReturnType()!!.isAssignableFrom(getReturnType()!!)
}
private fun Method.isReturnTypeTheSameAs(other: Method): Boolean {
if (getReturnType() == null && other.getReturnType() == null) return true
if (getReturnType() == null || other.getReturnType() == null) return false
return other.getReturnType() == getReturnType()
}
fun Method.findBridgeMethod(): Method? {
if (isBridge()) return null
return declaringClass.getDeclaredMethods().find {
it != this &&
isBridge() &&
it.getName() == getName() &&
isReturnTypeCovariant(it) &&
areParametersCovariant(it)
}
}
fun Method.isOverridenBy(other: Method): Boolean {
val bridge = findBridgeMethod()
if (bridge != null) return bridge!!.isOverridenBy(other)
return getName() == other.getName() &&
isOverridableIn(other.declaringClass) &&
!other.isAccessMoreRestrictiveThan(this) &&
isReturnTypeTheSameAs(other) &&
areParametersTheSameAs(other);
}
fun Method.findOverridenMethod() = findOverridenMethodIn(declaringClass)
private fun Method.findOverridenMethodIn(cls: Class<*>): Method? {
val superclasses = arrayListOf(cls.superclass)
cls.getInterfaces().forEach { superclasses.add(it) }
for (superclass in superclasses) {
if (superclass == null) continue
var overriden = superclass.getDeclaredMethods().find { it.isOverridenBy(this) }
if (overriden != null) return overriden
overriden = findOverridenMethodIn(superclass)
if (overriden != null) return overriden
}
return null;
}
//Workaround for bug KT-3194
//See: http://youtrack.jetbrains.com/issue/KT-3194
inline val Class<*>.superclass: Class<*>?
get() = (this as Class<Any>).getSuperclass()
inline val Member.isFinal: Boolean
get() = Modifier.isFinal(getModifiers())
inline val Member.isPrivate: Boolean
get() = Modifier.isPrivate(getModifiers())
inline val Member.isStatic: Boolean
get() = Modifier.isStatic(getModifiers())
inline val Member.isPublic: Boolean
get() = Modifier.isPublic(getModifiers())
inline val Member.isAbstract: Boolean
get() = Modifier.isAbstract(getModifiers())
inline val Member.declaringClass: Class<*>
get() = getDeclaringClass()
inline fun Member.isAccessMoreRestrictiveThan(other: Member) = restrictionLevel > other.restrictionLevel
private inline val Member.restrictionLevel: Int
get() = when {
isPrivate -> 0
isProtected -> 2
isPublic -> 3
else -> 1 //No scope modifiers = package private
}
//Note: Does not consider the declaring class "inheritability"
inline val Method.isOverridable: Boolean
get() = !isFinal && !isPrivate && !isStatic
inline val Member.isPackageVisible: Boolean
get() = !isPrivate
inline val Member.isSubclassVisible: Boolean
get() = isPublic || isProtected
It's 100% compatible with Java so I guess it can be easily translated. It should theoretically handle every tricky cases of overriding such as generics, scopes, incompatible signatures etc. I hope this will help!