Как вызвать метод суперкласса с использованием рефлексии в 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?
Выполнение кода, содержащегося в строке