Think in Java之多态
多态通过分离做什么和怎么做,从另一角度将接口和实现分离开来。多态方法调用允许亿欧汇总类型表现出与其他相似类型之间的区别。
向上转型
导出类从基类继承而来,所以存在于基类中的接口也必定存在于导出类中。将导出类转换为基类称作“向上转型”,上是按照类的继承关系为方向的。
增加扩展性
当我们想调用基类的某个方法时,不同的导出类都会有不同的行为,只需覆盖该方法即可。当有新的导出类时我们不必定义新方法去做事情直接覆盖基类的方法版本编译器自动帮我们定位到时机调用的导出类的方法。
我们只在基类中写一个简单方法, 它只接受基类类型为参数,而不是特殊的导出类,也就是说在调用方法的时候不考虑导出类的存在,编写的代码只是和基类打交道,具体的行为编译器帮我们自动判断该调用哪个方法版本。
方法绑定
编译器如何得知这个基类类型是哪个导出类类型呢?比如DShape有三个导出类:Circle,Rect,Triangle;并且这三个导出类都复写了父类的方法,编译器如何得知真实的类型是这三个中的哪个呢?
编译器也无法得知。
将一个方法调用同一个方法主体关联起来被称作绑定。在程序执行前进行的绑定叫做前期绑定(由编译器和连接程序实现),该绑定方式是面向过程语言无默认的绑定方式,C语言只有一种绑定方式就是前期绑定;多态其实就是因为前期绑定的问题而不知道该调用哪个方法版本,还有一种绑定叫做后期绑定:在运行时根据对象的类型进行绑定,这种绑定方式的实现是通过在对象中增加“某些类型信息”来实现的。
Java中除了stratic方法和final方法(private方法也属于final方法)外,其他方法都属于后期绑定。final方法可以有效的关闭“动态绑定”,告诉编译器不需要动态绑定因为其不会被复写也就不需要运行时绑定。
多态的坑
与其说是多态的坑不如说是不了解多态
private方法
当我们在基类中定义一个draw方法修饰符为private。在导出类中再定义一个同名的公开的draw方法。使用多态调用draw方法时会发现调用的是基类的方法。
原因
由于private方法会被编译器认为是final方法,采用的是前期绑定的方式对导出类是屏蔽的;子类的draw方法是一个全新的方法,既然基类中的draw方法对子类不可见,也就不能发生重载覆盖,在加上其声明类型为基类,所以调用的是基类的版本
结论
只有非private的方法才会被覆盖。在导出类中,对于基类的private方法采用不同的方法名字。
static方法
和上面类似,导出类和基类定义同名的静态方法,使用多态时调用的方法版本是基类的static方法。
原因
静态方法是与类绑定的而不时和单个对象绑定的。
结论
static方法不具有多态性
非final方法
private方法也即final方法。要保证在构造器中调用的方法是final的,比如在基类构造器中调用的方法是可被覆盖的那么就会出现问题,很可能可被覆盖的方法此时需要的变量还未初始化(对象构造顺序)因此出现奇怪现象。
所以要保证构造器中调用的方法是不可被覆盖的,也就是final的
Field的访问
向上转型过程中,域访问操作都由编译器自动解析,因此也属于前期绑定。
使用多态访问同名属性时访问的是基类的属性(动态绑定只针对与方法调用);在方法中访问属性时会用到动态绑定,默认的域是实际类型,如果想访问基类的属性需要在方法中使用super.属性
转载于:Think in Java之多态 - 掘金 (juejin.cn)