如何使用Javareflection调用超类方法
我有两个class。
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()
如果您正在使用JDK7,则可以使用MethodHandle来实现此目的:
public class Test extends Base { public static void main(String[] args) throws Throwable { MethodHandle h1 = MethodHandles.lookup().findSpecial(Base.class, "toString", MethodType.methodType(String.class), Test.class); MethodHandle h2 = MethodHandles.lookup().findSpecial(Object.class, "toString", MethodType.methodType(String.class), Test.class); System.out.println(h1.invoke(new Test())); // outputs Base System.out.println(h2.invoke(new Test())); // outputs Base } @Override public String toString() { return "Test"; } } class Base { @Override public String toString() { return "Base"; } }
这是不可能的。 在java中派发方法总是考虑对象的运行时types,即使使用reflection。 查看Method.invoke的javadoc ; 特别是本节:
如果底层方法是一个实例方法,则使用dynamic方法查找来调用该方法,如“Java语言规范第二版”第15.12.4.4节中所述; 特别是基于目标对象的运行时types的覆盖将会发生。
build立在@ java4script的答案,我注意到,如果你试图从子类之外做这个技巧(即,你通常会调用super.toString()
开始),你会得到一个IllegalAccessException
。 in
方法允许您仅在某些情况下(例如,当您使用与Base
和Sub
相同的包进行调用时)绕过此方法。 我发现的一般情况下唯一的解决方法是极端(显然不可移植)的黑客攻击:
package p; public class Base { @Override public String toString() { return "Base"; } } package p; public class Sub extends Base { @Override public String toString() { return "Sub"; } } import p.Base; import p.Sub; import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodHandles; import java.lang.invoke.MethodType; import java.lang.reflect.Field; public class Demo { public static void main(String[] args) throws Throwable { System.out.println(new Sub()); Field IMPL_LOOKUP = MethodHandles.Lookup.class.getDeclaredField("IMPL_LOOKUP"); IMPL_LOOKUP.setAccessible(true); MethodHandles.Lookup lkp = (MethodHandles.Lookup) IMPL_LOOKUP.get(null); MethodHandle h1 = lkp.findSpecial(Base.class, "toString", MethodType.methodType(String.class), Sub.class); System.out.println(h1.invoke(new Sub())); } }
印花
Sub Base
你不能,你需要一个超级类的实例,因为方法派发的方式在Java中有效。
你可以尝试这样的事情:
import java.lang.reflect.*; class A { public void method() { System.out.println("In a"); } } class B extends A { @Override public void method() { System.out.println("In b"); } } class M { public static void main( String ... args ) throws Exception { A b = new B(); b.method(); b.getClass() .getSuperclass() .getMethod("method", new Class[]{} ) .invoke( b.getClass().getSuperclass().newInstance() ,new Object[]{} ); } }
但最有可能的是,这是没有意义的,因为你会丢失b
的数据。
你不能这样做。 这意味着多态性不起作用。
你需要一个A
的实例。 您可以通过superclass.newInstance()
创build一个,然后使用类似BeanUtils.copyProperties(..)
(来自commons-beanutils)的方式传输所有字段。 但是这是一个“黑客” – 你应该修复你的devise,所以你不需要这样做。
我不知道怎么做,当你想要做的包括库的技巧,因为reflection不起作用,但对于我自己的代码,我会做这个简单的解决方法:
public class A { public Object method() {...} } public class B extends A { @Override public Object method() {...} public Object methodSuper() { return super.method(); } }
对于简单的情况,这是可以的,对于一些自动调用不是那么多。 例如,当你有一个链
A1 super A2 super A3 ... super An
inheritance类,全部重写一个方法m。 然后从A1的实例调用m将需要太多糟糕的编码:-)
直到最近谷歌才推出新的Gradle工具。 所以它只能在Android中工作,但要听:
为了更详细地描述问题,我会说:
我有几个场景,超类可能有或没有实现一个特定的方法,在那里我想用reflection调用超类方法。
你可以用gradle的口味来解决这个问题。 对于每种情况,创build你的应用程序的不同风味,并决定是否要调用超级方法。
例如,如果仅在api 13中引入超类方法,则为api 13+创build一个风格,并且仅在那里调用超类方法。
该解决scheme通过build议应用程序的多构build表示消除了使用reflection调用超类方法的需要。
在调用invokespecial之前,可以使用不同的指针创build一个字节码序列。
调用任何对象的super.toString()方法如下所示:
ALOAD X ;X = slot of object reference of the object to access INVOKESPECIAL java/lang/Object.toString ()Ljava/lang/String; POP
这样创build一个包含必要对象的匿名类是可能的。