Java 中,当父类属于抽象类的时候利用反射技术是自动调用子类的方法吗?

如果是,那么如果有多个子类继承此父类时,怎么知道调用的是哪个子类的方法呢?或者怎么调用指定的子类的方法?
关注者
34
被浏览
5533
题主想问的是这样的场景么?
import java.lang.reflect.Method;

abstract class AbstractFoo {
  public abstract void foo();
}

class FooA extends AbstractFoo {
  @Override
  public void foo() { System.out.println("FooA"); }
}

class FooB extends AbstractFoo {
  @Override
  public void foo() { System.out.println("FooB"); }
}

public class Test {
  public static void main(String[] args) throws Exception {
    AbstractFoo foo1 = new FooA();
    AbstractFoo foo2 = new FooB();
    Method mfoo = AbstractFoo.class.getMethod("foo"); // 1
    mfoo.invoke(foo1);                                // 2
    mfoo.invoke(foo2);                                // 3
  }
}
题主的问题是不是在这样的例子中,通过(1)得到了对应 AbstractFoo.foo() 的Method对象后,在(2)、(3)处的调用是如何知道要调用哪个版本的方法的?

如果是的话,答案很简单:实际上就跟普通虚方法的调用的语义是一模一样的。
Java的反射API中,如果我们得到的Method对象对应的是一个虚方法,则对它调用Method.invoke()的时候,它就会把传入的第一个参数作为“被调用对象”(receiver),并以它的实际类型为依据来做虚方法分派。
所以上述的(2)处,根据foo1所指向的对象的实际类型来做虚方法分派,自然就会调用到FooA.foo()这个版本的实现上。(3)也是同理。

Java的反射API并不允许我们对虚方法强制指定版本,而是必须遵循正常的虚方法分派的语义去调用。所以例如说,如果上面的例子里main()里有:
    FooB.class.getMethod("foo").invoke(foo1);
则它会在运行时抛出IllegalArgumentException异常,因为foo1指向的对象的实际类型是FooA,而这里我们想强制以它为被调用对象来调用FooB.foo(),是不被允许的。
然后,如果我们把上面例子中的AbstractFoo.foo()改为带有实现:
abstract class AbstractFoo {
  public void foo() { System.out.println("AbstractFoo"); };
}
则(2)、(3)处还是会运行得到跟原来一样的结果。我们不能通过 AbstractFoo.foo() 对应的Method对象就强制调用这个版本的方法,而还是得乖乖按照虚方法分派去调用传入的“被调用对象”的实际类型上合适的版本。