How to call a superclass method using Java reflection

47,078

Solution 1

If you are using JDK7, you can use MethodHandle to achieve this:

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";
  }
}

Solution 2

It's not possible. Method dispatching in java always considers the run-time type of the object, even when using reflection. See the javadoc for Method.invoke; in particular, this section:

If the underlying method is an instance method, it is invoked using dynamic method lookup as documented in The Java Language Specification, Second Edition, section 15.12.4.4; in particular, overriding based on the runtime type of the target object will occur.

Solution 3

Building on @java4script’s answer, I noticed that you get an IllegalAccessException if you try to do this trick from outside the subclass (i.e., where you would normally be calling super.toString() to begin with). The in method allows you to bypass this only in some cases (such as when you are calling from the same package as Base and Sub). The only workaround I found for the general case is an extreme (and clearly nonportable) 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()));
    }
}

printing

Sub
Base

Solution 4

You can't do that. It would mean polymorphism is not working.

You need an instance of A. You can create one by superclass.newInstance() and then transfer all fields with something like BeanUtils.copyProperties(..) (from commons-beanutils). But that's a 'hack' - you should instead fix your design so that you don't need that.

Solution 5

You can't, you'll need an instance of the super class because of the way methods dispatching works in Java.

You could try something like this:

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[]{}  );

    }
}

But most likely, it doesn't make sense, because you'll loose the data in b.

Share:
47,078
Admin
Author by

Admin

Updated on August 01, 2020

Comments

  • Admin
    Admin almost 4 years

    I have two classes:

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

    I have an instance of B. How do I call A.method() from b? Basically, the same effect as calling super.method() from 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);
    

    But the above code will still invoke B.method().