如何使用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()

Ted asked 2020-08-10T22:19:38Z
7个解决方案
27 votes

如果使用的是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 answered 2020-08-10T22:19:43Z
10 votes

这是不可能的。 Java中的方法分派始终考虑对象的运行时类型,即使使用反射时也是如此。 请参见javadoc中的Method.invoke;。 特别是本节:

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

Greg answered 2020-08-10T22:20:08Z
7 votes

在@ java4script的答案的基础上,我注意到,如果您尝试从子类外部(即,通常从此处开始调用super.toString())进行此技巧,则会得到IllegalAccessExceptionin方法仅允许您在某些情况下(例如,从与BaseSub相同的程序包进行调用时)绕过此方法。 对于一般情况,我发现的唯一解决方法是极端的(显然是不可移植的)hack:

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
Jesse Glick answered 2020-08-10T22:20:33Z
4 votes

不能,因为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中的数据。

OscarRyz answered 2020-08-10T22:21:02Z
4 votes

你不能那样做。 这将意味着多态性无法正常工作。

您需要一个实例A。您可以通过superclass.newInstance()创建一个实例,然后使用诸如BeanUtils.copyProperties(..)(来自commons-beanutils)的内容传输所有字段。 但这是一个“黑客”-您应该修复设计,以免使用它。

Bozho answered 2020-08-10T22:21:27Z
2 votes

当您想为包含的库完成技巧时,我不知道该怎么做,因为反射不起作用,但是对于我自己的代码,我将执行以下简单的解决方法:

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。 然后在An的实例上从A1调用m会需要太多错误的编码:-)

reggie_7 answered 2020-08-10T22:21:56Z
0 votes

您可以在调用invokespecial之前使用其他this指针创建字节码序列。

调用任何对象的super.toString()方法就像:

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

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

Martin Kersten answered 2020-08-10T22:22:24Z
translate from https://stackoverflow.com:/questions/5411434/how-to-call-a-superclass-method-using-java-reflection