如何使用Java反射调用超类方法
我有两个类:
public class A {
public Object method() {...}
}
public class B extends A {
@Override
public Object method() {...}
}
我有一个 B
实例。如何从 b
调用 A.method()
?基本上,与从 B
调用 super.method()
的效果相同。
B b = new B();
Class<?> superclass = b.getClass().getSuperclass();
Method method = superclass.getMethod("method", ArrayUtils.EMPTY_CLASS_ARRAY);
Object value = method.invoke(obj, ArrayUtils.EMPTY_OBJECT_ARRAY);
但上面的代码仍然会调用B.method()
。
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(8)
如果您使用的是JDK7,您可以使用MethodHandle来实现这一点:
If you are using JDK7, you can use MethodHandle to achieve this:
基于@java4script的答案,我注意到如果您尝试从子类外部(即您通常会调用
的地方)执行此技巧,您会得到一个
)。IllegalAccessException
首先是 super.toString()in
方法允许您仅在某些情况下绕过此问题(例如,当您从与Base
和Sub
相同的包进行调用时)。我发现的一般情况的唯一解决方法是一个极端的(并且显然不可移植的)黑客:打印
Building on @java4script’s answer, I noticed that you get an
IllegalAccessException
if you try to do this trick from outside the subclass (i.e., where you would normally be callingsuper.toString()
to begin with). Thein
method allows you to bypass this only in some cases (such as when you are calling from the same package asBase
andSub
). The only workaround I found for the general case is an extreme (and clearly nonportable) hack:printing
这是不可能的。 java 中的方法分派始终考虑对象的运行时类型,即使在使用反射时也是如此。请参阅 Method.invoke 的 javadoc;特别是本节:
It's not possible. Method dispatching in java always considers the run-time type of the object, even when using reflection. See the javadoc for Method.invoke; in particular, this section:
你不能,因为 Java 中方法分派的工作方式,你需要一个超类的实例。
您可以尝试这样的操作:
但很可能,它没有意义,因为您会丢失
b
中的数据。You can't, you'll need an instance of the super class because of the way methods dispatching works in Java.
You could try something like this:
But most likely, it doesn't make sense, because you'll loose the data in
b
.你不能那样做。这意味着多态性不起作用。
您需要一个
A
实例。您可以通过superclass.newInstance()
创建一个实例,然后使用BeanUtils.copyProperties(..)
(来自 commons-beanutils)之类的方法传输所有字段。但这是一个“黑客”——你应该修复你的设计,这样你就不需要它了。You can't do that. It would mean polymorphism is not working.
You need an instance of
A
. You can create one bysuperclass.newInstance()
and then transfer all fields with something likeBeanUtils.copyProperties(..)
(from commons-beanutils). But that's a 'hack' - you should instead fix your design so that you don't need that.当您想要对包含的库执行此操作时,我不知道该怎么做,因为反射不起作用,但对于我自己的代码,我会执行这个简单的解决方法:
对于简单的情况,这是可以的,对于某些自动调用则没有那么多。例如,当您有一系列
继承类时,所有类都重写一个方法 m。然后在 An 的实例上从 A1 调用 m 将需要太多糟糕的编码:-)
I don't know how to do it in the case when You want to do the trick for included libraries, because the reflection doesn't work, but for my own code I would do this simple workaround:
For simple cases this is OK, for some automatic invocation not so much. For instance, when You have a chain
of inheriting classes, all overriding a method m. Then invoking m from A1 on an instance of An would require too much bad coding :-)
您可以在调用 调用特殊。
调用任何对象的 super.toString() 方法就像:
这样创建一个包含必要对象的匿名类是可能的。
You can create a byte code sequence using a different this pointer before calling invokespecial.
Calling the super.toString() method of any object is like:
This way creating an anonymous class containing the necessary object is possible.
如果您无法修改任一类,则需要使用反射来使用方法句柄。您需要使用
invokespecial
调用来调用超级方法,这可以通过 MethodHandles 来完成。 MethodHandles.lookup() 方法使用调用者类创建一个 Lookup 实例,并且只允许来自该类的特殊调用。要从任何类调用超级方法,请获取 Lookup 的构造函数:创建它的实例,并使用 unreflect 和已在内部指向 A 的反射方法对象(或使用 findSpecial):
使用类 A 或 B 作为参数传递 构造函数中指定的类和 unreflectSpecial/findSpecial 的最后一个参数必须相同,尽管它是哪个类并不重要,只要它是 A(或 A 本身)的子类即可。
生成的 MethodHandle 会忽略任何覆盖,因此它将始终调用属于原始 Method 对象(在本例中为 A)的方法或指定为 findSpecial 第一个参数的类。调用该方法时,传递 B 对象作为第一个参数以及任何其他参数:
If you can't modify either class, you need to use reflection to use method handles. You need to use
invokespecial
calls to call super methods which can be done with MethodHandles. The MethodHandles.lookup() method creates a Lookup instance with the caller class and only permits special calls from that class. To call super methods from any class, get the constructor of Lookup:Create an instance of it with class A or B passed as the parameter, and use unreflect with the reflected method object which already points to A internally (or use findSpecial):
Note that the class specified in the constructor and the last argument of unreflectSpecial/findSpecial must be the same, although it doesn't matter which class it is as long as it's a subclass of A (or A itself).
The resulting MethodHandle ignores any overrides so it will always call the method belonging to the original Method object (A in this case) or the class specified as the first argument of findSpecial. When invoking the method, pass the B object as the first parameter, and any additional arguments: