在Java的多态特性中,我们经常会使用父类引用指向子类实例,但父类引用只能直接调用父类中声明的方法,子类独有的方法无法直接通过父类引用访问。这时候可以通过Class对象的反射能力,或者合理的类型转换来实现对子类方法的访问。

基础场景准备
首先我们先定义父类和子类,父类包含一个公共方法,子类继承父类并新增一个独有的方法,同时重写父类的公共方法,方便后续演示。
// 定义父类
class Parent {
public void parentMethod() {
System.out.println("这是父类的方法");
}
}
// 定义子类,继承父类
class Child extends Parent {
// 重写父类方法
@Override
public void parentMethod() {
System.out.println("子类重写的父类方法");
}
// 子类独有的方法
public void childOnlyMethod() {
System.out.println("这是子类独有的方法");
}
}通过父类引用获取实际Class对象访问子类方法
当我们有父类引用指向子类实例时,可以通过getClass()方法获取实际对象的Class对象,再通过反射调用子类的方法。这种方式不需要提前知道具体的子类类型,适合运行时动态处理的场景。
import java.lang.reflect.Method;
public class Test {
public static void main(String[] args) throws Exception {
// 父类引用指向子类实例
Parent parentRef = new Child();
// 调用父类可访问的方法,实际执行子类重写后的版本
parentRef.parentMethod();
// 通过父类引用获取实际对象的Class对象,即Child的Class对象
Class<?> childClass = parentRef.getClass();
// 获取子类独有的方法,参数为方法名和参数类型,无参方法传空数组
Method childMethod = childClass.getMethod("childOnlyMethod");
// 调用方法,传入实际对象实例,无参方法传空数组
childMethod.invoke(parentRef);
}
}运行上述代码会先输出子类重写的父类方法,再输出这是子类独有的方法,说明通过Class对象的反射成功调用了子类独有的方法。
直接通过子类Class对象创建实例访问方法
如果我们知道具体的子类类型,也可以直接获取子类的Class对象,创建实例后调用子类方法,这种方式更直观,适合明确子类类型的场景。
import java.lang.reflect.Method;
public class Test2 {
public static void main(String[] args) throws Exception {
// 获取子类的Class对象
Class<?> childClass = Class.forName("Child");
// 创建子类实例,需要强转为父类类型或者子类类型
Parent parentRef = (Parent) childClass.getDeclaredConstructor().newInstance();
// 调用重写的方法
parentRef.parentMethod();
// 如果需要调用子类独有方法,可以强转为子类类型,或者通过反射调用
// 方式1:强转为子类类型
Child child = (Child) parentRef;
child.childOnlyMethod();
// 方式2:继续通过反射调用
Method childMethod = childClass.getMethod("childOnlyMethod");
childMethod.invoke(parentRef);
}
}两种方式对比
两种方式各有适用场景,我们可以通过下面的表格快速了解差异:
| 实现方式 | 适用场景 | 优点 | 缺点 |
|---|---|---|---|
| 父类引用获取实际Class对象反射调用 | 运行时不知道具体子类类型,只有父类引用 | 无需提前知道子类类型,灵活度高 | 需要处理反射相关的异常,性能略低于直接调用 |
| 子类Class对象创建实例调用 | 明确知道子类类型,需要创建实例调用方法 | 代码更直观,可直接强转调用,性能更好 | 需要提前知道子类类型,灵活性较低 |
注意事项
- 使用反射调用方法时,需要处理
NoSuchMethodException、IllegalAccessException、InvocationTargetException等异常,实际开发中要做好异常处理。 - 如果子类方法是私有的,需要使用
getDeclaredMethod获取方法,并且设置method.setAccessible(true)才能调用。 - 强制类型转换的前提是父类引用实际指向的就是该子类的实例,否则会抛出
ClassCastException异常。
反射虽然灵活,但会破坏封装性,同时带来一定的性能开销,实际开发中如果不是必要场景,优先使用多态和合理的类型设计,避免过度使用反射。