2011-01-27 55 views
16

Làm thế nào để tìm hiểu xem một phương pháp có bị ghi đè bởi các lớp con không?Java: Làm cách nào để tìm ra phương thức được ghi đè từ lớp cơ sở?

Ví dụ,

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

} 

Với một thể hiện của B, làm thế nào để tôi biết nếu có các lớp thừa kế đã ghi đè phương pháp m() như B2?

Cập nhật: Câu hỏi của tôi không rõ ràng. Trên thực tế, tôi đã cố gắng để hỏi nếu điều này là có thể mà không cần phải suy nghĩ để phản ánh. Kiểm tra này được thực hiện trong một vòng lặp chặt chẽ và nó được sử dụng trong một hack hiệu suất để tiết kiệm một vài chu kỳ CPU.

+1

Tất nhiên làm thế nào bạn đang không phải là việc của tôi, nhưng nếu bạn thực sự cần thông tin này, có OO thiết kế lẽ nghèo rình rập trong bóng tối – biziclop

+0

Tôi không nghĩ rằng nó có thể mà không cần đến sự phản ánh. Và tôi đồng ý với biziclop, đó là dấu hiệu của thiết kế kém. –

Trả lời

5

Câu hỏi này giúp chứng minh làm thế nào để có được các thông tin trong đó lớp phương pháp mà thuộc về:

How to quickly determine if a method is overridden in Java

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

Ngay sau khi bạn ghi đè lên một phương thức trong lớp con, phương thức này trở thành một phương thức được khai báo của lớp này, do đó _clazz.getMethod ("myMethod"). GetDeclaringClass() _ sẽ trả về chính lớp con. Tôi đoán bạn có thể nhận được các lớp học siêu và gọi lại phương pháp để kiểm tra xem phương pháp vẫn còn xuất hiện và rút ra kết luận của bạn từ đó. – Hardy

+0

Điều này chắc chắn sẽ không hoạt động như được giải thích trong phần bình luận ở trên. –

+0

-1 Đừng đánh cắp câu trả lời từ một câu hỏi khác. Gắn cờ câu hỏi này là bản sao của câu hỏi khác. –

7

Tôi nghĩ rằng câu trả lời cho đến nay là giả định rằng bạn có một phương thức và đang cố gắng xác định xem phương thức đó có bị ghi đè trong một lớp hay không.

Tuy nhiên, câu hỏi thực tế được hỏi là "Cho một cá thể B, làm cách nào để biết liệu có bất kỳ lớp dẫn xuất nào có phương thức ghi đè m() như B2?"

Điều này không thể thực hiện bằng phương pháp Java chuẩn, vì Java không tải các lớp cho đến khi chúng được tham chiếu. Ví dụ, giả sử bạn có một trình nạp lớp URL tải từ một jar (hoặc nhiều lọ) trên mạng. Java không có ý tưởng những gì các lớp học được chứa trong các tập tin jar mạng, hãy để một mình cho dù họ xảy ra để ghi đè lên một phương pháp cụ thể. Tôi nghĩ rằng tôi đã thấy các tiện ích trong cộng đồng Apache sẽ cố gắng tìm kiếm toàn bộ hệ thống phân cấp của các trình nạp lớp để tập hợp tất cả các lớp có sẵn, nhưng điều này nghe có vẻ hơi tệ đối với tôi. Đối với một điều, nó sẽ kích hoạt mọi khối khởi tạo tĩnh đơn cho mỗi lớp trong JVM.

Có một số tiện ích như Giao diện nhà cung cấp dịch vụ để liệt kê tên lớp trong thư mục META-INF của jar thực hiện giao diện nhất định, có thể bạn nên xem tuyến đường đó.

+0

điều này là chính xác. – irreputable

+0

Câu hỏi ban đầu dường như yêu cầu một kiểm tra tồn tại (không thể) như vậy, mặc dù tôi đoán đây không phải là những gì áp phích thực sự có ý nghĩa. Nói xấu. –

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

Ở trên sẽ không hoạt động nếu phương thức được xác định trong giao diện. –

+0

Hoặc nếu nó là chung chung. –

4

Nâng cao bài bởi Pavel Savara, đây là phiên bản của tôi về phương pháp mà làm việc cho các giao diện quá:

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

Sẽ không hoạt động đối với các lớp con, giao diện gián tiếp, v.v. –

+1

Hoặc các phương pháp chung –

4

Đây là giải pháp của tôi, nó được viết bằng Kotlin (ngôn ngữ 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 

Nó tương thích 100% với Java vì vậy tôi đoán nó có thể được dịch dễ dàng. Nó lý thuyết nên xử lý mọi trường hợp khó khăn của trọng tài như generics, phạm vi, chữ ký không tương thích vv Tôi hy vọng điều này sẽ giúp!

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

hỗ trợ java Chú thích. Nếu bạn không chắc chắn nếu phương pháp thực hiện được ghi đè từ lớp cơ sở.

Chỉ cần sử dụng từ khóa @Override trước khi phương thức của bạn bắt đầu ở lớp con.

Nếu phương pháp đó thực sự có thể là phương thức ghi đè thì nó sẽ biên dịch tốt. nếu không, nó sẽ đưa ra lỗi.

đơn giản :)

+0

Chú thích @verver không thể xem được bằng phản hồi do RetentionPolicy –

+0

Điều này không trả lời được câu hỏi. Vui lòng đọc [Làm cách nào để viết câu trả lời hay?] (Http://stackoverflow.com/help/how-to-answer) trước khi cố gắng trả lời thêm câu hỏi. –

Các vấn đề liên quan