【问题标题】:Java: How to find if a method is overridden from base class? [duplicate]Java:如何查找一个方法是否被基类覆盖? [复制]
【发布时间】:2011-06-16 20:11:38
【问题描述】:

如何判断一个方法是否被子类覆盖?

例如,

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

}

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

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

【问题讨论】:

  • 当然,你如何编码不关我的事,但如果你真的需要这些信息,那么可能隐藏在阴影中的是糟糕的 OO 设计
  • 我认为不借助反思是不可能的。我同意 biziclop,这是设计不佳的标志。

标签: java inheritance methods overriding introspection


【解决方案1】:

这个问题有助于演示如何获取该方法所属的类的信息:

How to quickly determine if a method is overridden in Java

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

【讨论】:

  • 一旦你在子类中重写了一个方法,这个方法就成为这个类的声明方法,因此 clazz.getMethod("myMethod").getDeclaringClass() 将返回子类本身。我想你可以获取超类并再次调用 get 方法来检查该方法是否仍然出现并从中得出你的结论。
  • 这肯定不会像上面评论中解释的那样工作。
  • -1 不要窃取另一个问题的答案。将此问题标记为与其他问题重复。
【解决方案2】:

我认为到目前为止的答案是假设您有一个方法并试图确定该方法是否在类中被覆盖。

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

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

我认为我在 Apache commons 中看到了一些实用程序,它们会尝试详尽地搜索类加载器的层次结构以组装所有可用类的列表,但这对我来说听起来是个很糟糕的主意。一方面,它将触发 JVM 中每个类的每个静态初始化块。

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

【讨论】:

  • 原始问题似乎确实要求进行这种(不可能的)存在性检查,尽管我猜这不是海报的实际意思。措辞不好。
【解决方案3】:

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

【讨论】:

  • 不适用于子类、间接接口等
【解决方案4】:

这是我的解决方案,它是用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

它与 Java 100% 兼容,所以我想它可以很容易地翻译。从理论上讲,它应该可以处理所有棘手的覆盖情况,例如泛型、作用域、不兼容的签名等。我希望这会有所帮助!

【讨论】:

【解决方案5】:
 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;
     }
 }

【讨论】:

  • 如果方法是在接口中定义的,上述方法将不起作用。
【解决方案6】:

Java 的反射 API 有一个 Method 类,该类有一个名为 getDeclaringClass() 的方法。这可能适用于您的需要。这是 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 支持注解。 如果您不确定实现的方法是否被基类覆盖。

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

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

      简单:)

      【讨论】:

      • @Override 注解由于其 RetentionPolicy 而无法通过反射看到
      • 这没有回答问题。在尝试回答更多问题之前,请阅读How do I write a good answer?
      猜你喜欢
      • 1970-01-01
      • 2011-01-19
      • 1970-01-01
      • 2015-08-29
      • 2012-03-29
      • 2014-04-16
      • 1970-01-01
      • 2013-07-13
      • 2014-03-14
      相关资源
      最近更新 更多