Java:如何查找是否从基类重写了一个方法?

ZZ *_*der 17 java methods inheritance overriding introspection

如何查明子类是否覆盖了某个方法?

例如,

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

}
Run Code Online (Sandbox Code Playgroud)

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

更新:我的问题不明确.实际上,我试图在不诉诸反思的情况下询问是否可行.这种检查是在一个紧凑的循环中完成的,它用于性能破解以节省一些CPU周期.

Jes*_*num 7

我认为到目前为止的答案是假设你有一个方法,并试图确定是否在类中重写该方法.

但是,问的实际问题是"给定B的实例,我如何知道任何派生类是否已经覆盖了方法m(),如B2?"

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

我认为我已经看到Apache公共中的实用程序会尝试详尽地搜索类加载器的层次结构来组合所有可用类的列表,但这听起来对我来说是一个非常糟糕的主意.首先,它将为JVM中的每个类触发每个静态初始化程序块.

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


Lit*_*ium 6

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

如何快速确定是否在Java中重写了方法

class.getMethod("myMethod").getDeclaringClass();
Run Code Online (Sandbox Code Playgroud)

  • 一旦覆盖子类中的方法,此方法就成为此类的声明方法,因此_clazz.getMethod("myMethod").getDeclaringClass()_将返回子类本身.我想你可以得到超级类并再次调用get方法来检查方法是否仍然出现并从中得出结论. (3认同)

小智 6

改进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;
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 不适用于子子类、间接接口等。 (3认同)

Sim*_* V. 5

这是我的解决方案,它是用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
Run Code Online (Sandbox Code Playgroud)

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


tro*_*tor 1

Java 的反射 API 有一个Method类,其中有一个名为 的方法getDeclaringClass()。这可能适合您的需要。这是 API:

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