可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试):
问题:
This question already has an answer here:
-
How to quickly determine if a method is overridden in Java
8 answers
How to find out if a method is overridden by child classes?
For example,
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());
}
}
Given an instance of B, how do I know if any derived classes have overridden method m() like B2?
Update: My question wasn't clear. Actually, I was trying to ask if this is possible without resorting to reflection. This check is done in a tight loop and it's used in a performance hack to save a few CPU cycles.
回答1:
I think the answers so far are assuming that you have a method and are trying to determine whether that method is overridden in a class.
However, the actual question asked was "Given an instance of B, how do I know if any derived classes have overridden method m() like B2?"
This is not possible to do using standard Java methodology, because Java does not load classes until they are referenced. For example, assume you have a URL classloader loading from a jar (or many jars) on the network. Java has no idea what classes are contained in those networked jar files, let alone whether they happen to override a particular method.
I think that I've seen utilities in Apache commons that will try to exhaustively search the hierarchy of classloaders to assemble a list of all available classes, but this sounds like a pretty bad idea to me. For one thing, it will trigger every single static initializer block for every class in the JVM.
There are some facilities like the Service Provider Interface to list class names in the jar's META-INF directory that implement a certain interface, maybe you should look at that route.
回答2:
This question helps demonstrate how to get the information of which class that method belongs to:
How to quickly determine if a method is overridden in Java
class.getMethod("myMethod").getDeclaringClass();
回答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:
Improving the post by Pavel Savara, here's my version of the method that works for interfaces too:
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:
Here is my solution, it's written in Kotlin (JVM language).
//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
It's 100% compatible with Java so I guess it can be easily translated. It should theoretically handle every tricky cases of overriding such as generics, scopes, incompatible signatures etc. I hope this will help!
回答6:
Java's reflection API has a Method
class which has a method called getDeclaringClass()
. That might work for what you need. Here is the 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 supports Annotations.
If you are not sure if method implemented is overridden from base class.
Just use @Override keyword before your method start in child class.
If that method really can a overridden method then it would compile fine.
otherwise it will give give error.
Simple :)