55

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().

aboger
  • 2,214
  • 6
  • 33
  • 47
Ted
  • 551
  • 1
  • 4
  • 3

8 Answers8

37

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";
  }
}
Nagaraja Thangavelu
  • 1,168
  • 1
  • 15
  • 31
java4script
  • 547
  • 6
  • 5
  • 1
    You should. It is not possible to call a *grandfather* method. Even by byte code manipulation. – Rafael Winterhalter Jan 22 '14 at 16:56
  • 1
    This only works when the class extends another. Is there a way to do this in any class? – zcaudate Feb 12 '14 at 05:33
  • As noted, `h2.invoke` does not work (behaves like `h1`), but still a very useful hack. – Jesse Glick Aug 08 '14 at 20:15
  • 1
    It is possible to call a grandparent method if you use reflection to get MethodHandles.Lookup.IMPL_LOOKUP, which doesn't perform access checks. demo: https://gist.github.com/nallar/2ac088d93fb411bd7240c9dd6c7cdc7f – Luna Aug 19 '17 at 23:49
13

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
Jesse Glick
  • 24,539
  • 10
  • 90
  • 112
  • Really clever solution, albeit quite dirty. – Carlo Pellegrini Sep 17 '15 at 15:00
  • A more generic solution would be: lkp.findSpecial(Base.class, "toString", MethodType.methodType(String.class), Base.class). Else it will be restricted to instances of type Sub or instances of subtypes of Sub. – tuga Mar 06 '17 at 12:27
10

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.

Greg
  • 33,450
  • 15
  • 93
  • 100
  • 4
    But how does super.method() work? I believe JVM holds both A.method and B.method bytecode, and it chooses to call A.method() if B.method calls super.method(). – Ted Mar 24 '11 at 20:53
  • https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-6.html#jvms-6.5.invokespecial – Martin Kersten Aug 12 '16 at 20:02
3

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.

Bozho
  • 588,226
  • 146
  • 1,060
  • 1,140
  • But how does super.method() work? I believe JVM holds both A.method and B.method bytecode, and it chooses to call A.method() if B.method calls super.method(). – Ted Mar 24 '11 at 20:46
  • @Ted super works that way, but you can only use it from the subclass. – Bozho Mar 24 '11 at 21:22
3

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.

OscarRyz
  • 196,001
  • 113
  • 385
  • 569
  • 1
    But how does super.method() work? I believe JVM holds both A.method and B.method bytecode, and it chooses to call A.method() if B.method calls super.method(). – Ted Mar 24 '11 at 21:52
  • 4
    It is an "special" instruction at the bytecode level ( just like a constructor ) that seems to be unavailable with reflection. The instruction is "invokeSpecial" see: http://java.sun.com/docs/books/jvms/second_edition/html/Instructions2.doc6.html – OscarRyz Mar 24 '11 at 23:07
2

I don't know how to do it in the case when You want to do the trick for included libraries, because the reflection doesn't work, but for my own code I would do this simple workaround:

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

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

    public Object methodSuper() {
        return super.method();
    }
}

For simple cases this is OK, for some automatic invocation not so much. For instance, when You have a chain

A1 super A2 super A3 ... super An 

of inheriting classes, all overriding a method m. Then invoking m from A1 on an instance of An would require too much bad coding :-)

reggie_7
  • 21
  • 1
  • 1
    This doesn't help you, when you have to use reflection (e.g. because methodSuper() did not exist in an earlier version of the platform you run on.) – Marcus Wolschon Nov 10 '11 at 11:18
1

You can create a byte code sequence using a different this pointer before calling invokespecial.

Calling the super.toString() method of any object is like:

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

This way creating an anonymous class containing the necessary object is possible.

Martin Kersten
  • 5,127
  • 8
  • 46
  • 77
-1

If you can't modify either class, you need to use reflection to use method handles. You need to use invokespecial calls to call super methods which can be done with MethodHandles. The MethodHandles.lookup() method creates a Lookup instance with the caller class and only permits special calls from that class. To call super methods from any class, get the constructor of Lookup:

var lookupConstructor = MethodHandles.Lookup.class.getDeclaredConstructor(Class.class);
lookupConstructor.setAccessible(true);

Create an instance of it with class A or B passed as the parameter, and use unreflect with the reflected method object which already points to A internally (or use findSpecial):

var lookup = lookupConstructor.newInstance(B.class);

var method = A.class.getMethod("method");
var mHandle = lookup.unreflectSpecial(method, B.class);
// OR
var mHandle = lookup.findSpecial(A.class, "method", MethodType.methodType(returnType), B.class);

Note that the class specified in the constructor and the last argument of unreflectSpecial/findSpecial must be the same, although it doesn't matter which class it is as long as it's a subclass of A (or A itself).

The resulting MethodHandle ignores any overrides so it will always call the method belonging to the original Method object (A in this case) or the class specified as the first argument of findSpecial. When invoking the method, pass the B object as the first parameter, and any additional arguments:

Object ret = mHandle.invoke(b);
NorbiPeti
  • 91
  • 2
  • 8