Java: How to find if a method is overridden from base class? [duplicate]
Asked Answered
G

8

20

How to find out if a method is overridden by child classes?

For example,

public class Test {

static public class B {
    public String m() {return "From B";};
}

static public class B1 extends B {

}

static public class B2 extends B {
    public String m() {return "from B2";};
}

/**
 * @param args
 * @throws FileNotFoundException 
 */
public static void main(String[] args)  {

    B b1 = new B1();
    System.out.println("b1 = " + b1.m());
    B b2 = new B2();
    System.out.println("b1 = " + b2.m());
}

}

Given an instance of B, how do I know if any derived classes have overridden method m() like B2?

Update: My question wasn't clear. Actually, I was trying to ask if this is possible without resorting to reflection. This check is done in a tight loop and it's used in a performance hack to save a few CPU cycles.

Guendolen answered 27/1, 2011 at 20:51 Comment(2)
Of course how you code is none of my business but if you actually need this information, there's probably poor OO design lurking in the shadowsTatary
I don't think it's possible without resorting to reflection. And I agree with biziclop, it's a sign of poor design.Pin
S
8

This question helps demonstrate how to get the information of which class that method belongs to:

How to quickly determine if a method is overridden in Java

class.getMethod("myMethod").getDeclaringClass();
Soper answered 27/1, 2011 at 20:56 Comment(3)
As soon as you override a method in the subclass this method becomes a declared method of this class, hence clazz.getMethod("myMethod").getDeclaringClass() will return the subclass itself. I guess you could get the super class and call get method again to check whether the method still appears and draw your conclusions from that.Strega
This is certainly not going to work as explained in the comment above.Raynaraynah
-1 Don't steal an answer from another question. Flag this question as a duplicate of the other.Lurette
C
7

I think the answers so far are assuming that you have a method and are trying to determine whether that method is overridden in a class.

However, the actual question asked was "Given an instance of B, how do I know if any derived classes have overridden method m() like B2?"

This is not possible to do using standard Java methodology, because Java does not load classes until they are referenced. For example, assume you have a URL classloader loading from a jar (or many jars) on the network. Java has no idea what classes are contained in those networked jar files, let alone whether they happen to override a particular method.

I think that I've seen utilities in Apache commons that will try to exhaustively search the hierarchy of classloaders to assemble a list of all available classes, but this sounds like a pretty bad idea to me. For one thing, it will trigger every single static initializer block for every class in the JVM.

There are some facilities like the Service Provider Interface to list class names in the jar's META-INF directory that implement a certain interface, maybe you should look at that route.

Cheerful answered 27/1, 2011 at 21:8 Comment(1)
The original question does seem to request such an (impossible) existential check, though I am guessing this is not what the poster actually meant. Poorly worded.Chirm
R
6

Improving the post by Pavel Savara, here's my version of the method that works for interfaces too:

public static boolean isMethodOverrriden(final Method myMethod) {
    Class<?> declaringClass = myMethod.getDeclaringClass();
    if (declaringClass.equals(Object.class)) {
        return false;
    }
    try {
        declaringClass.getSuperclass().getMethod(myMethod.getName(), myMethod.getParameterTypes());
        return true;
    } catch (NoSuchMethodException e) {
        for (Class<?> iface : declaringClass.getInterfaces()) {
            try {
                iface.getMethod(myMethod.getName(), myMethod.getParameterTypes());
                return true;
            } catch (NoSuchMethodException ignored) {

            }
        }
        return false;
    }
}
Raynaraynah answered 4/3, 2013 at 16:23 Comment(1)
Will not work for subsubclasses, indirect interfaces, etc.Chirm
C
4
 public static boolean isMethodOverrriden(Method myMethod) {
     Class<?> declaringClass = myMethod.getDeclaringClass();
     if (declaringClass.equals(Object.class)) {
         return false;
     }
     try {
         declaringClass.getSuperclass().getMethod(myMethod.getName(), myMethod.getParameterTypes());
         return true;
     } catch (NoSuchMethodException e) {
         return false;
     }
 }
Cotenant answered 27/4, 2011 at 12:29 Comment(1)
The above would not work if the method is defined in an interface.Raynaraynah
G
4

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!

Gamble answered 8/3, 2013 at 20:8 Comment(1)
Not all heroes wear capes. Thank you so much for this, I was just about pulling my hair out trying to figure out how to do this for my advanced reflection project.Acephalous
S
1

Java's reflection API has a Method class which has a method called getDeclaringClass(). That might work for what you need. Here is the API:

http://download.oracle.com/javase/6/docs/api/java/lang/reflect/Method.html#getDeclaringClass()

Stygian answered 27/1, 2011 at 20:55 Comment(0)
P
-1
private static boolean isMethodImplemented(Object obj, String name)
{
    try
    {
        Class<? extends Object> clazz = obj.getClass();

        return clazz.getMethod(name).getDeclaringClass().equals(clazz);
    }
    catch (SecurityException e)
    {
        log.error("{}", e);
    }
    catch (NoSuchMethodException e)
    {
        log.error("{}", e);
    }

    return false;
}
Popeyed answered 28/10, 2013 at 14:25 Comment(0)
J
-3

java supports Annotations. If you are not sure if method implemented is overridden from base class.

Just use @Override keyword before your method start in child class.

If that method really can a overridden method then it would compile fine. otherwise it will give give error.

Simple :)

Jeep answered 9/8, 2016 at 9:19 Comment(2)
@Override annotation can't be seen by reflection due to its RetentionPolicyUnblock
This does not answer the question. Please read How do I write a good answer? before attempting to answer more questions.Myrtice

© 2022 - 2024 — McMap. All rights reserved.