Подтвердить что ты не робот

Java: как определить, переопределен ли метод из базового класса?

Как узнать, переопределен ли метод дочерними классами?

Например,

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, как узнать, имеют ли какие-либо производные классы переопределенный метод m() как B2?

Обновление: мой вопрос был не ясен. Собственно, я пытался спросить, возможно ли это, не прибегая к размышлениям. Эта проверка выполняется в жесткой петле, и она используется для взлома производительности, чтобы сэкономить несколько циклов процессора.

4b9b3361

Ответ 1

Я думаю, что до сих пор ответы предполагают, что у вас есть метод и вы пытаетесь определить, переопределен ли этот метод в классе.

Тем не менее, фактический вопрос был задан "Учитывая экземпляр B, откуда я знаю, если какие-либо производные классы имеют переопределенный метод m(), такой как B2?"

Это невозможно сделать с использованием стандартной методологии Java, поскольку Java не загружает классы до тех пор, пока они не будут указаны. Например, предположим, что вы загружаете загрузчик класса URL из банки (или многих банок) в сети. Java не имеет представления о том, какие классы содержатся в этих сетевых файлах jar, не говоря уже о том, заменяет ли они какой-либо конкретный метод.

Я думаю, что я видел утилиты в сообществах Apache, которые будут пытаться исчерпывающе искать иерархию загрузчиков классов, чтобы собрать список всех доступных классов, но это звучит для меня довольно плохо. Во-первых, он будет запускать каждый отдельный статический блок инициализатора для каждого класса в JVM.

Есть некоторые возможности, такие как интерфейс поставщика услуг, чтобы перечислять имена классов в каталоге метаданных META-INF, которые реализуют определенный интерфейс, возможно, вам стоит посмотреть на этот маршрут.

Ответ 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

Улучшение сообщения 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;
    }
}

Ответ 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

Он на 100% совместим с Java, поэтому, я думаю, его можно легко перевести. Он должен теоретически обрабатывать все сложные случаи переопределения, такие как дженерики, области, несовместимые подписи и т.д. Надеюсь, это поможет!

Ответ 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, прежде чем ваш метод начнет работать в дочернем классе.

Если этот метод действительно может переопределить метод, он будет компилироваться в порядке. в противном случае это даст ошибку.

Простой:)