49

私は2つのクラスを持っています:

public class A {
    public Object method() {...}
}

public class B extends A {
    @Override
    public Object method() {...}
}

のインスタンスがありますBA.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()ます。

4

8 に答える 8

33

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";
  }
}
于 2013-03-28T04:46:45.373 に答える
10

不可能です。Java でのメソッドのディスパッチでは、リフレクションを使用している場合でも、常にオブジェクトの実行時の型が考慮されます。Method.invokeの javadoc を参照してください。特に、このセクション:

基礎となるメソッドがインスタンス メソッドである場合は、Java 言語仕様、第 2 版、セクション 15.12.4.4 に記載されているように、動的メソッド ルックアップを使用して呼び出されます。特に、ターゲット オブジェクトのランタイム タイプに基づくオーバーライドが発生します。

于 2011-03-23T20:57:51.377 に答える
10

@java4script の回答に基づいて、サブクラスの外部(つまり、通常は最初に呼び出す場所)IllegalAccessExceptionからこのトリックを実行しようとすると、エラーが発生することに気付きました。このメソッドでは、一部のケース (および と同じパッケージから呼び出している場合など) でのみこれをバイパスできます。一般的なケースで私が見つけた唯一の回避策は、極端な(そして明らかに移植性がない)ハックです:super.toString()inBaseSub

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
于 2014-08-08T20:55:47.877 に答える
3

そんなことはできません。これは、ポリモーフィズムが機能していないことを意味します。

のインスタンスが必要ですA。1つずつ作成してから、(commons-beanutilsから)のsuperclass.newInstance()ようなものですべてのフィールドを転送できます。BeanUtils.copyProperties(..)しかし、それは「ハック」です。代わりに、それが不要になるように設計を修正する必要があります。

于 2011-03-23T20:52:43.953 に答える
3

できません。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

于 2011-03-23T20:53:36.787 に答える
2

リフレクションが機能しないため、含まれているライブラリに対してトリックを実行したい場合の方法はわかりませんが、自分のコードでは次の簡単な回避策を実行します。

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 を呼び出すには、あまりにも多くの悪いコーディングが必要になります:-)

于 2011-08-17T22:26:23.927 に答える
0

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

このようにして、必要なオブジェクトを含む匿名クラスを作成できます。

于 2016-08-12T20:05:33.160 に答える