• 向上转型和向下转型


    多态的条件

    继承。

    在多态中必须存在有继承关系的子类和父类。


    重写。

    子类对父类中某些方法进行重新定义,在调用这些方法时就会调用子类的方法。


    向上转型。

    在多态中需要将子类的引用赋给父类对象,只有这样该引用才能够具备技能调用父类的方法和子类的方法
    继承也可以替换为实现接口。


    向上转型

    子类引用的对象转换为父类类型称为向上转型。通俗地说就是是将子类对象转为父类对象。此处父类对象可以是接口。

    案例驱动

    public class Animal {
        public void eat(){
            System.out.println("animal eatting...");
        }
    }
    
    public class Cat extends Animal{
    
        public void eat(){
    
            System.out.println("我吃鱼");
        }
    }
    
    public class Dog extends Animal{
    
        public void eat(){
    
            System.out.println("我吃骨头");
        }
    
        public void run(){
            System.out.println("我会跑");
        }
    }
    
    public class Main {
    
        public static void main(String[] args) {
    
            Animal animal = new Cat(); //向上转型
            animal.eat();
    
            animal = new Dog();
            animal.eat();
        }
    
    }
    
    //结果:
    //我吃鱼
    //我吃骨头

    这就是向上转型,Animal animal = new Cat();将子类对象Cat转化为父类对象Animal。这个时候animal这个引用调用的方法是子类方法。

    转型过程中需要注意的问题

    向上转型时,子类单独定义的方法会丢失。比如上面Dog类中定义的run方法,当animal引用指向Dog类实例时是访问不到run方法的,animal.run()会报错。
    子类引用不能指向父类对象。Cat c = (Cat)new Animal()这样是不行的。

    向上转型的好处

    减少重复代码,使代码变得简洁。
    提高系统扩展性。 举个例子:比如我现在有很多种类的动物,要喂它们吃东西。如果不用向上转型,那我需要这样写:

    public void eat(Cat c){
        c.eat();
    }
    
    public void eat(Dog d){
        d.eat();
    }
    //......
    
    eat(new Cat());
    eat(new Cat());
    eat(new Dog());
    //......

    一种动物写一个方法,如果我有一万种动物,我就要写一万个方法,写完大概猴年马月都过了好几个了吧。好吧,你很厉害,你耐着性子写完了,以为可以放松一会了,突然又来了一种新的动物,你是不是又要单独为它写一个eat方法?开心了么?

    那如果我使用向上转型呢?我只需要这样写:

    public void eat(Animal a){
        a.eat();
    }
    
    eat(new Cat());
    eat(new Cat());
    eat(new Dog());
    //.....

    恩,搞定了。代码是不是简洁了许多?而且这个时候,如果我又有一种新的动物加进来,我只需要实现它自己的类,让他继承Animal就可以了,而不需要为它单独写一个eat方法。是不是提高了扩展性?

    向下转型

    与向上转型相对应的就是向下转型了。向下转型是把父类对象转为子类对象。(请注意!这里是有坑的。)

    案例驱动

    //还是上面的animal和cat dog
    Animal a = new Cat();
    Cat c = ((Cat) a);
    c.eat();
    //输出  我吃鱼
    Dog d = ((Dog) a);
    d.eat();
    // 报错 : java.lang.ClassCastException:com.chengfan.animal.Cat cannot be cast to com.chengfan.animal.Dog
    Animal a1 = new Animal();
    Cat c1 = ((Cat) a1);
    c1.eat();
    // 报错 : java.lang.ClassCastException:com.chengfan.animal.Animal cannot be cast to com.chengfan.animal.Cat

    向下转型注意事项

    下转型的前提是父类对象指向的是子类对象(也就是说,在向下转型之前,它得先向上转型)
    向下转型只能转型为本类对象(猫是不能变成狗的)。
    大概你会说,我特么有病啊,我先向上转型再向下转型?? 我们回到上面的问题:喂动物吃饭,吃了饭做点什么呢?不同的动物肯定做不同的事,怎么做呢?

    public void eat(Animal a){
        if(a instanceof Dog){  
            Dog d = (Dog)a;
            d.eat();
            d.run();//狗有一个跑的方法      
        } 
        if(a instanceof Cat){  
            Cat c = (Cat)a;
            c.eat();
            System.out.println("我也想跑,但是不会"); //猫会抱怨    
        } 
        a.eat();//其他动物只会吃
    }
    
    eat(new Cat());
    eat(new Cat());
    eat(new Dog());
    //.....

    经典案例分析多态

    基本的多态和转型我们都会了,最后加点餐。看一个经典案例:.

    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


    //能看懂这个结果么?先自分析一下。
    当父类对象引用变量引用子类对象时,被引用对象的类型决定了调用谁的成员方法,引用变量类型决定可调用的方法。如果子类中没有覆盖该方法,那么会去父类中寻找。
    可能读起来比较拗口,我们先来看一个简单的例子:

    class X {
        public void show(Y y){
            System.out.println("x and y");
        }
    
        public void show(){
            System.out.println("only x");
        }
    }
    
    class Y extends X {
        public void show(Y y){
            System.out.println("y and y");
        }
        public void show(int i){
    
        }
    }
    
    class main{
        public static void main(String[] args) {
            X x = new Y();
            x.show(new Y());
            x.show();
        }
    }
    //结果
    //y and y
    //only x

    Y继承了X,覆盖了X中的show(Y y)方法,但是没有覆盖show()方法。

    这个时候,引用类型为X的x指向的对象为Y,这个时候,调用的方法由Y决定,会先从Y中寻找。执行x.show(new Y());,该方法在Y中定义了,所以执行的是Y里面的方法;

    但是执行x.show();的时候,有的人会说,Y中没有这个方法啊?它好像是去父类中找该方法了,因为调用了X中的方法。

    事实上,Y类中是有show()方法的,这个方法继承自X,只不过没有覆盖该方法,所以没有在Y中明确写出来而已,看起来像是调用了X中的方法,实际上调用的还是Y中的。

    这个时候再看上面那句难理解的话就不难理解了吧。X是引用变量类型,它决定哪些方法可以调用;show()和show(Y y)可以调用,而show(int i)不可以调用。Y是被引用对象的类型,它决定了调用谁的方法:调用y的方法。
    上面的是一个简单的知识,它还不足以让我们理解那个复杂的例子。我们再来看这样一个知识:

    继承链中对象方法的调用的优先级:this.show(O)、super.show(O)、this.show((super)O)、super.show((super)O)。
    如果你能理解这个调用关系,那么多态你就掌握了。我们回到那个复杂的例子:

    abcd的关系是这样的:C/D —> B —> A

    我们先来分析4 : a2.show(b)

    首先,a2是类型为A的引用类型,它指向类型为B的对象。A确定可调用的方法:show(D obj)和show(A obj)。
    a2.show(b) ==> this.show(b),这里this指的是B。
    然后.在B类中找show(B obj),找到了,可惜没用,因为show(B obj)方法不在可调用范围内,this.show(O)失败,进入下一级别:super.show(O),super指的是A。
    在A 中寻找show(B obj),失败,因为没用定义这个方法。进入第三级别:this.show((super)O),this指的是B。
    在B中找show((A)O),找到了:show(A obj),选择调用该方法。
    输出:B and A 如果你能看懂这个过程,并且能分析出其他的情况,那你就真的掌握了。
    我们再来看一下9:b.show(d)

    首先,b为类型为B的引用对象,指向类型为B的对象。没有涉及向上转型,只会调用本类中的方法。
    在B中寻找show(D obj),方法。现在你不会说没找到了吧?找到了,直接调用该方法。
    输出 A and D。

  • 相关阅读:
    php实现导出excel功能(转)
    nginx + php-fpm 开启 PATH_INFO 模式
    openEuler安装mariadb后进程查看有[ERROR] mysqld: Server GSSAPI错误
    CentOS 8开放防火墙端口
    ssh连接PVE下debian11的LXC容器非常慢之解决方法
    debian11安装SRS教程
    用python下载网页或图片
    解决Debian vim鼠标无法选中、复制问题
    在Ubuntu系统中安装pymssql
    Mariadb设置允许远程链接
  • 原文地址:https://www.cnblogs.com/lvchengda/p/12620451.html
Copyright © 2020-2023  润新知