面向对象——多态与向上、向下转型机制

前言

开头先回忆一下,面向对象的三大特征:封装(数据抽象)、继承、多态。为什么多态排在最后一位,因为它是以前面两个为前提的,尤其是继承。

多态概念梳理

多态本质在于 同一种行为的多样化表达 (这句话不禁让我想起了基因的多样性表达,这是现实世界物种多态的原因)。

对于某一种行为而言,它的多样性体现在两种可能:
1. 行为主体相同,行为受体不同——同样是待客,为什么他对别人温柔对你凶?
2. 行为受体相同,行为主体不同——同样是吃肉,人是煮熟吃,动物生吃。

这是我们在生活中的例子,现在我们迁移到编程中的多态!

我们是不是可以把一个方法称为一种行为,他的调用者我们称为“行为主体” ,他的参数我们称为”行为受体”呢,而把函数体看作是行为的具体内容?

编程中,多态分为 编译时多态 和 运行时多态,也称为 前期绑定动态绑定

注:存在说法认为多态仅仅限定于运行时的多态,本人认为还是以“同一种行为的多样化表达”的基准,说法是死的,思想是活的,大家也不用在这方面纠结。

编译时多态:编程中的体现是方法重载,调用者是明确的,调用方法有多种,我们根据参数来确定调用哪个方法,从而体现多样性。
运行时多态:根源于继承之后的方法重写,多个子类都继承了相同父类,并且都对父类中某个普通方法进行了重写,从而体现多样性。


实验

首先,先创建继承关系如下:
image_1clkqo76a8jg14i41g8u17cfkj4d.png-12.3kB
image_1clkqmn0m4q01mr8j91urq1cc230.png-11.5kB
image_1clkqklrcrni1ov928h1ig2mqq2j.png-12.3kB

转型

能实现转型,是运行时多态的必要条件之一。
编译的机制:
根据你引用指定的类型去搜索你的方法是否存在,如果该引用类型中不存在调用方法,报错。
运行时机制
运行时,是根据引用所指的具体对象类型来调用方法。
当我们将父类中的study()方法注释:
image_1clkqhovg3n9oh41sihtsf6t1p.png-12.5kB
运行时:
image_1clkqjqgvkmn19uucks159mdce26.png-17.9kB
从机器级角度分析这个问题:
编译时根据对象引用确定该方法的符号引用名称,运行时根据对象引用指向的对象进行符号引用的重定位。

向上转型

如果你是一个大学生,那么你肯定也是一名学生。
我们对你所属的范围进行了一次周延扩大,这就是一次向上转型。

用处:
1.提高了程序的扩充性,不需要写一些重复的代码。
2.增加代码简洁性,可阅读性。

假设我们有一个老师,他既教高中生,也教大学生,不同学生上课方式不同,正常那我们不是要进行方法重载为每个学生量身定制一个方法吗?但是我们可以让他们自适应:
image_1clkqu5hefucqoh1e6rrce6eu5d.png-7.8kB
image_1clkrf32l12p71ige1j2b1i1tdmo8h.png-20kB
结果:
image_1clkrffjc1oje1p5m13nctm2ele8u.png-10.7kB

向上转型进阶分析:
如果方法调用者和方法参数都是具有继承关系的类型,这个向上转型是怎么转的呢?
观查测试样例:

image_1cll2h6gt4ai51q14dg1fpfrlka8.png-25.3kB

image_1cll2enkf14eg1ulvpktijhh9n9r.png-13.9kB

输出:
    A and B

当我们将A类中的show(B)注释掉:
image_1cll2isddv8jiig1s68106ed9gal.png-24.1kB
image_1cll2kq401lqu14di10dsjmtt56b9.png-13.5kB

输出:
    B and A

规律总结:

先是调用者由下到上向上转型查找对应方法; 若没找到,每次将调用参数向上转型一次,再寻找对应方法。

向下转型(注意点)

你是一个学生,但不一定是大学生。向下转型,一定要注意类型的匹配。
为什么又会有向下转型?向上转型的弊端在于被调用方法受到局限,而当你又想调用子类独有的方法,你就必须用 向下转型 转回去。
所以向下转型一定是先有向上转型作铺垫的。
但是,这边注意一点,你对象是什么类型,你转回去也应该是什么类型。

人->动物->猪 ×
人->动物->人 √
image_1clkr5u6k18rvntl1im01vq11sh26q.png-26.7kB
编译没有报错,但运行一定抛出转型异常。
image_1clkramai1mmrr34rcs1nmpuui77.png-10kB
正确做法:
image_1clkrcdf327cvvg1og11j0d1g1h7k.png-13kB


牛刀小试

根据我们之前总结的规律,看看你的答案是否正确吧:

class A {
    public String show(D obj) {
        return ("A and D");
    }

    public String show(A obj) {
        return ("A and A");
    }

}

class B extends A{
    public String show(B obj){
        return ("B and B");
    }

    public String show(A obj){
        return ("B and A");
    }
}

class C extends B{

}

class D extends B{

}

public class Demo {
    public static void main(String[] args) {
        A a1 = new A();
        A a2 = new B();
        B b = new B();
        C c = new C();
        D d = new D();

        System.out.println("1--" + a1.show(b));
        System.out.println("2--" + a1.show(c));
        System.out.println("3--" + a1.show(d));
        System.out.println("4--" + a2.show(b));
        System.out.println("5--" + a2.show(c));
        System.out.println("6--" + a2.show(d));
        System.out.println("7--" + b.show(b));
        System.out.println("8--" + b.show(c));
        System.out.println("9--" + b.show(d));
    }
}

输出:
    1--A and A
    2--A and A
    3--A and D
    4--B and A
    5--B and A
    6--A and D
    7--B and B
    8--B and B
    9--A and D
posted @ 2018-08-24 12:35  顾杰伟  阅读(252)  评论(0编辑  收藏  举报