Java: How to find if a method is overridden from b

2019-01-12 05:25发布

问题:

This question already has an answer here:

  • How to quickly determine if a method is overridden in Java 8 answers

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.

回答1:

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.



回答2:

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();


回答3:

 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;
     }
 }


回答4:

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;
    }
}


回答5:

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!



回答6:

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()



回答7:

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;
}


回答8:

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 :)