Java:如何查找方法是否被基类覆盖?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/4821704/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-30 08:11:05  来源:igfitidea点击:

Java: How to find if a method is overridden from base class?

javainheritancemethodsoverridingintrospection

提问by ZZ Coder

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?

给定一个 B 的实例,我怎么知道是否有任何派生类像 B2 一样重写了方法 m()?

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.

更新:我的问题不清楚。实际上,我试图在不诉诸反思的情况下询问这是否可行。这个检查是在一个紧密的循环中完成的,它用于性能黑客以节省几个 CPU 周期。

回答by Jesse Barnum

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?"

但是,实际提出的问题是“给定 B 的一个实例,我如何知道是否有任何派生类像 B2 一样重写了方法 m()?”

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.

使用标准 Java 方法无法做到这一点,因为 Java 在类被引用之前不会加载它们。例如,假设您有一个从网络上的 jar(或多个 jar)加载的 URL 类加载器。Java 不知道那些联网的 jar 文件中包含哪些类,更不用说它们是否碰巧覆盖了特定的方法。

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.

我认为我已经看到 Apache 公共资源中的实用程序会尝试彻底搜索类加载器的层次结构以组装所有可用类的列表,但这对我来说听起来是一个非常糟糕的主意。一方面,它将触发 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.

有一些工具,比如服务提供者接口,可以在 jar 的 META-INF 目录中列出实现某个接口的类名,也许你应该看看那个路由。

回答by Lithium

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

如何快速确定一个方法是否在 Java 中被覆盖

class.getMethod("myMethod").getDeclaringClass();

回答by Simon V.

Here is my solution, it's written in Kotlin(JVM language).

这是我的解决方案,它是用Kotlin(JVM 语言)编写的。

//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!

它与 Java 100% 兼容,所以我想它可以轻松翻译。它理论上应该处理所有棘手的覆盖情况,例如泛型、范围、不兼容的签名等。我希望这会有所帮助!

回答by Marek Potociar

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

改进Pavel Savara的帖子,这里是我的方法版本,也适用于接口:

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

回答by Pavel Savara

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

回答by troutinator

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

Java 的反射 API 有一个Method类,该类有一个名为getDeclaringClass(). 这可能适用于您的需要。这是API:

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

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

回答by kayz1

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

回答by Ketan

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

java 支持注解。如果您不确定实现的方法是否从基类覆盖。

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

只需在您的方法在子类中启动之前使用 @Override 关键字。

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

如果该方法真的可以被覆盖,那么它会编译得很好。否则它会给出错误。

Simple :)

简单的 :)