如何使用Java反射调用超类方法

如何使用Java反射调用超类方法,java,reflection,overriding,superclass,Java,Reflection,Overriding,Superclass,我有两门课: 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().ge

我有两门课:

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);
bb=新的B();
Class superclass=b.getClass().getSuperclass();
Method=superclass.getMethod(“Method”,ArrayUtils.EMPTY\u CLASS\u数组);
Object value=method.invoke(obj,ArrayUtils.EMPTY\u Object\u ARRAY);

但是上面的代码仍然会调用
B.method()

您不能这样做。这意味着多态性不起作用


您需要一个
A
的实例。您可以通过
超类.newInstance()
创建一个,然后使用
BeanUtils.copyProperties(..)
(来自commons BeanUtils)之类的内容传输所有字段。但是这是一个“黑客”——你应该修改你的设计,这样你就不需要了。

你不能,你需要一个超类的实例,因为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
中丢失数据,这是不可能的。java中的方法调度总是考虑对象的运行时类型,即使在使用反射时也是如此。看,;特别是,本节:

如果基础方法是 实例方法,使用 动态方法查找,如中所述 Java语言规范, 第二版,第15.12.4.4节;在里面 特别是,基于 将显示目标对象的运行时类型 发生


我不知道如何在您想要对包含的库执行此技巧时执行此操作,因为反射不起作用,但对于我自己的代码,我将执行以下简单的解决方法:

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 

在继承类中,全部重写方法m。然后在一个实例上从A1调用m将需要太多糟糕的编码:-)

如果您使用的是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";
  }
}

基于@java4script的回答,我注意到如果您尝试从子类外部执行此操作(即,通常在开始时调用
super.toString()
),会得到一个
IllegalAccessException
)。方法中的
仅在某些情况下(例如,当您从与
Base
Sub
相同的包调用时)允许您绕过此选项。对于一般情况,我发现的唯一解决方法是一种极端的(显然是不可移植的)黑客攻击:

印刷品

Sub
Base

在调用之前,可以使用不同的this指针创建字节码序列

调用任何对象的super.toString()方法如下:

ALOAD X ;X = slot of object reference of the object to access
INVOKESPECIAL java/lang/Object.toString ()Ljava/lang/String;
POP

这种方法可以创建一个包含必要对象的匿名类。

如果不能修改任何一个类,则需要使用反射来使用方法句柄。您需要使用
invokespecial
调用来调用超级方法,这可以通过MethodHandles完成。MethodHandles.lookup()方法使用caller类创建查找实例,并且只允许从该类进行特殊调用。要从任何类调用超级方法,请获取Lookup的构造函数:

var lookupConstructor = MethodHandles.Lookup.class.getDeclaredConstructor(Class.class);
lookupConstructor.setAccessible(true);
使用传递的类A或B作为参数创建其实例,并对已在内部指向的反射方法对象使用unreflect(或使用findSpecial):

请注意,构造函数中指定的类和unreflectSpecial/findSpecial的最后一个参数必须相同,尽管它是哪个类并不重要,只要它是a的子类(或a本身)

结果MethodHandle忽略任何重写,因此它将始终调用属于原始method对象(在本例中为A)的方法或指定为findSpecial第一个参数的类。调用该方法时,将B对象作为第一个参数传递,并传递任何其他参数:

Object ret = mHandle.invoke(b);

但是super.method()是如何工作的呢?我相信JVM同时拥有A.method和B.method字节码,如果B.method调用super.method(),它会选择调用A.method()。@Ted super是这样工作的,但您只能从子类使用它。但是super.method()是如何工作的呢?我相信JVM同时拥有A.method和B.method字节码,如果B.method调用super.method(),它会选择调用A.method()。但是super.method()是如何工作的呢?我相信JVM同时持有A.method和B.method字节码,如果B.method调用super.method(),它会选择调用A.method()。这是字节码级别的“特殊”指令(就像构造函数一样),在反射中似乎不可用。指令是“invokeSpecial”,请参见:当您必须使用反射时(例如,因为methodSuper()在您运行的平台的早期版本中不存在),这对您没有帮助。您应该这样做。不可能调用外祖父方法。甚至通过字节码操作。这只在类扩展另一个类时有效。在任何类中都有这样做的方法吗?如前所述,
h2.invoke
不起作用(行为类似于
h1
),但仍然是一种非常有用的黑客行为。如果使用反射来获取MethodHandles.Lookup.IMPL\u Lookup,则可以调用祖父母方法,它不执行访问检查。演示:非常聪明的解决方案,尽管相当脏。更通用的解决方案是:lkp.findSpecial(Base.class,“toString”、MethodType.MethodType(String.class)、Base.class)。否则,它将被限制为Sub类型的实例或Sub的子类型的实例。
var lookup = lookupConstructor.newInstance(B.class);

var method = A.class.getMethod("method");
var mHandle = lookup.unreflectSpecial(method, B.class);
// OR
var mHandle = lookup.findSpecial(A.class, "method", MethodType.methodType(returnType), B.class);
Object ret = mHandle.invoke(b);