你可能忽略的多态问题

203 阅读3分钟

多态:多态通过分离做什么和怎么做,从另一角度将接口和实现分离出来,有些文章将重载视作编译时多态(其实个人感觉并没有多态的特性),将重写视为运行时多态

先来看一段代码:

class Father {
    protected String type = "Father";

    public Father() {
    }

    public void getType() {
        System.out.println(type);
    }
}

class Son extends Father {
    protected String type = "Son";
    public Son() {
    }

    @Override
    public void getType() {
        System.out.println(type);
    }
}

public class TestDynamicBind {

    public void getType(Father father) {
        System.out.println("this is father interface");
        father.getType();
    }

    public void getType(Son son) {
        System.out.println("this is son interface");
        son.getType();
    }

    public static void main(String[] args) {
        Father son = new Son();
        System.out.println(son.type);   --1、Father
        son.getType();  -- 2、Son
        TestDynamicBind bind = new TestDynamicBind();
        bind.getType(son); -- 3、this is father interface/Son  
        bind.getType(new Son()); --4、this is son interface/Son  
    }
}

程序的执行结果为:

Father
Son
this is father interface
Son
this is son interface
Son
  • 1打印type属性,注意域和静态方法是无法使用多态的,因此打印域值为Father
  • 2通过对象的实例方法打印type属性,虽然变量son的声明类型为Father,但实际为Son类型对象,执行实例方法时会根据调用对象的方法表(如果对象为子类,改写了父类的方法,那么子类和父类的那些同名的方法共享一个方法表项,所有继承父类的子类的方法表中,重写的父类所定义的方法的偏移量也总是一个定值,这样 JVM 在调用实例方法时只需要指定调用方法表中的第几个方法即可)实际调用Son class的getType方法
  • 3调用TestDynamicBind的getType方法,注意该类有两个getType方法(也就是包含两个重载的getType方法),运行时应该调哪一个呢,其实编译时就决定了(所以个人觉得并不具备多态的特性),因为运行时是根据参数类型决定调用方法的,而参数类型是编译时期决定的,虽然对象实际为Son类型,后面会展示相关字节码,因此走入了getType(Father father)方法,但实际调用对象的getType方法时由于该对象为Son类型而调用了Son的getType方法,输出Son
  • 4直接构造了new Son()对象传入TestDynamicBind对象的getType方法,这种方式和Son son = new Son();bind.getType(son);的效果是一致的,因此走入了TestDynamicBind的getType(Son son)方法且输出Son

上字节码

main

  • 上面是main方法的字节码,可以看到TestDynamicBind的两次getType调用(后两个标注)都是编译期决定了参数类型(LFather/LSon),实际也确定了调用哪个方法,虽然字节码命令为invokevirtual
  • 对于对象实例方法的多态调用,继承父类和实现接口的实现机制虽有不同(继承的方法在父类和子类的方法表项的位置是相同的,而实现接口的方法在不同实现类的方法表项的位置很可能是不同的),但实际调用时都走了各自对象的具体实现方法

欢迎关注微信订阅号