Как вызвать метод суперкласса с использованием рефлексии в Java
Я сталкиваюсь с проблемой в Java, связанной с вызовом метода суперкласса. У меня есть два класса:
public class A {
    public Object method() {...}
}
public class B extends A {
    @Override
    public Object method() {...}
}
Я создал экземпляр класса B, и теперь мне нужно вызвать A.method() из экземпляра b. То есть, мне нужно добиться такого же эффекта, как если бы я использовал super.method() внутри класса B.
Вот какой код я использовать:
B b = new B();
Class<?> superclass = b.getClass().getSuperclass();
Method method = superclass.getMethod("method", ArrayUtils.EMPTY_CLASS_ARRAY);
Object value = method.invoke(b, ArrayUtils.EMPTY_OBJECT_ARRAY);
Однако, приведенный выше код по-прежнему вызывает метод B.method(), а не A.method(). Как мне правильно вызвать A.method() из экземпляра B?
4 ответ(ов)
Если вы используете 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()));   // выводит Base
        System.out.println(h2.invoke(new Test()));   // выводит Base
    }
    @Override
    public String toString() {
        return "Test";
    }
}
class Base {
    @Override
    public String toString() {
        return "Base";
    }
}
В этом коде вы создаете два MethodHandle: один для метода toString из класса Base и второй для метода toString из класса Object. При вызове этих методов через invoke, оба они возвращают строку "Base", так как MethodHandle действительно вызывает версию метода, реализованную в родительском классе Base. Теперь, если вы хотите получить результат от переопределенного метода toString в классе Test, вам просто нужно использовать invoke для экземпляра Test без указания конкретного MethodHandle.
Вы не можете сделать этого. Вам нужен экземпляр суперкласса из-за того, как работает диспетчеризация методов в 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(), а затем передать все поля с помощью чего-то вроде BeanUtils.copyProperties(..) (из библиотеки commons-beanutils). Однако это всего лишь "хаки" — вам следует улучшить свой дизайн, чтобы избежать необходимости в этом.
Я не знаю, как это сделать в случае, когда вы хотите применить этот трюк для включенных библиотек, поскольку рефлексия не работает, но для своего собственного кода я бы использовал такой простой способ:
public class A {
    public Object method() {...}
}
public class B extends A {
    @Override
    public Object method() {...}
    public Object methodSuper() {
        return super.method();
    }
}
Для простых случаев это нормально, но для автоматического вызова не очень. Например, когда у вас есть цепочка
A1 супер A2 супер A3 ... супер An 
наследующих классов, все переопределяющих метод m. Тогда вызов метода m из A1 на экземпляре An потребует слишком много неаккуратного кода 😃
Ошибка «Необходимо переопределить метод суперкласса» после импорта проекта в Eclipse
Как создать обобщённый массив в Java?
Получить обобщённый тип класса во время выполнения
Какие проблемы следует учитывать при переопределении equals и hashCode в Java?
Выполнение кода, содержащегося в строке