Как вызвать метод суперкласса, используя отражение Java - PullRequest
42 голосов
/ 23 марта 2011

У меня есть два класса.

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(obj, ArrayUtils.EMPTY_OBJECT_ARRAY);

Но приведенный выше код все равно вызовет B.method ()

Ответы [ 7 ]

27 голосов
/ 28 марта 2013

Если вы используете 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";
  }
}
10 голосов
/ 23 марта 2011

Это невозможно.Метод диспетчеризации в java всегда учитывает тип объекта во время выполнения, даже при использовании отражения.Смотрите javadoc для Method.invoke ;в частности, этот раздел:

Если базовый метод является методом экземпляра, он вызывается с использованием динамического поиска метода, как описано в Спецификации языка Java, второе издание, раздел 15.12.4.4;в частности, произойдет переопределение на основе типа времени выполнения целевого объекта.

7 голосов
/ 09 августа 2014

Опираясь на ответ @ java4script, я заметил, что вы получите IllegalAccessException, если попытаетесь сделать этот трюк из за пределами подкласса (т. Е. С того места, где вы обычно вызываете super.toString() для начала ). Метод 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
4 голосов
/ 23 марта 2011

Вы не можете, вам понадобится экземпляр суперкласса из-за того, как диспетчеризация методов работает в 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.

4 голосов
/ 23 марта 2011

Вы не можете этого сделать. Это означало бы, что полиморфизм не работает.

Вам нужен экземпляр A. Вы можете создать его с помощью superclass.newInstance() и затем передать все поля с чем-то вроде BeanUtils.copyProperties(..) (из commons-beanutils). Но это «хак» - вместо этого вы должны исправить свой дизайн, чтобы он вам не понадобился.

2 голосов
/ 18 августа 2011

Я не знаю, как это сделать в том случае, если вы хотите сделать трюк для включенных библиотек, потому что отражение не работает, но для моего собственного кода я бы сделал этот простой обходной путь:

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.Тогда вызов m из A1 для экземпляра An потребует слишком много плохого кодирования: -)

0 голосов
/ 12 августа 2016

Вы можете создать последовательность байтовых кодов, используя другой указатель this, перед вызовом invokespecial .

Вызов метода super.toString () для любого объекта выглядит следующим образом:

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

Таким образом, возможно создание анонимного класса, содержащего необходимый объект.

...