2011-01-27 139 views
16

如何找出一個方法是否被子類覆蓋?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的一個實例,如何知道如果任何派生類已重寫方法米()之類B2?

更新:我的問題不清楚。其實,我試圖在不訴諸反思的情況下這樣做是否可行。這個檢查是在一個緊密的循環中完成的,並且它被用於性能攻擊以節省幾個CPU週期。

+1

當然,你的代碼是怎麼關我的事,但如果你確實需要這些信息,有可能是窮人OO設計潛伏在暗處 – biziclop 2011-01-27 21:15:25

+0

我不認爲這是可能的,而不訴諸反思。我同意biziclop,這是一個設計不佳的標誌。 – 2011-01-28 07:53:07

回答

5

這個問題有助於說明如何獲得該方法屬於哪個類別的信息:

How to quickly determine if a method is overridden in Java

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

只要覆蓋子類中的方法,此方法就成爲此類的聲明方法,因此_clazz.getMethod(「myMethod」)。getDeclaringClass()_將返回子類本身。我想你可以得到超類,再次調用get方法來檢查方法是否仍然出現,並從中得出結論。 – Hardy 2012-01-20 15:57:12

+0

這肯定不會像上面評論中所解釋的那樣起作用。 – 2013-03-04 16:07:33

+0

-1不要從另一個問題竊取答案。將這個問題標記爲另一個的重複。 – 2014-04-04 15:32:37

7

我認爲到目前爲止的回答都假設你有一個方法並試圖確定該方法是否在類中被覆蓋。

但是,問的實際問題是「給定B的一個實例,我怎麼知道是否有任何派生類有重寫方法m()像B2?」

使用標準Java方法無法做到這一點,因爲Java在被引用之前不會加載類。例如,假設您從網絡上的jar(或許多jar)加載URL類加載器。 Java不知道這些聯網的jar文件中包含了哪些類,更不用說他們是否碰巧覆蓋了特定的方法。

我認爲我已經看到了Apache公用程序,它將嘗試徹底搜索類加載器的層次結構以組裝所有可用類的列表,但這聽起來對我來說是一個非常糟糕的主意。首先,它會觸發JVM中每個類的每個靜態初始化塊。

有一些像Service Provider Interface這樣的工具可以在jar的META-INF目錄中列出實現特定接口的類名,也許你應該看看那個路由。

+0

這是正確的。 – irreputable 2011-01-27 21:32:22

4
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; 
    } 
} 
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%兼容,所以我想它可以很容易地翻譯。它理論上應該處理每一個重寫的例子,比如泛型,範圍,不兼容的簽名等。我希望這會有所幫助!

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

java支持註解。 如果您不確定是否從基類覆蓋實施的方法。

只需在子類中的方法啓動之前使用@Override關鍵字。

如果該方法真的可以重寫的方法,那麼它會編譯好。 否則會給出錯誤。

簡單:)

相關問題