多态的应用与转型(以Java为例)

98 阅读4分钟

多态的成员访问

  • 构造方法:同继承一样,子类会通过super访问父类构造方法
  • 成员变量:编译看左边(父类),执行看右边(父类)

只去查找父类的变量,父类中没有我们要调用的变量,那么就会报错

  • 成员方法:编译看左边(父类),执行看右边(父类)

方法执行时,先执行子类重写的方法,如果子类没有重写,则动态的去父类查找

多态的优缺点

1 多态的优缺点 :

  1. 优点

提高代码的扩展性

  1. 缺点

不能调用子类特有的功能。在下面的代码中,你无法调用a.lookname( )/a.catchMouse( )/a.sleep( )方法,因为程序编译看的是父类,而这些方法是子类特有的功能,父类当中不存在,因此就会报错。

public class Test02 {
    public static void main(String[] args) {
        useAnimal(new Dog());
        useAnimal(new Cat());
        useAnimal(new Pig());
    }
public static void useAnimal(Animal a) {
    // 这就是多态的用法,原本需要编写像下面一样的三个测试类,但是现在只需给方法传入一个Animal类
    // 即可调用全部的子类方法
    a.eat();
}

//    // 测试狗类
//    public static void useAnimal(Dog dog) {
//        dog.eat();
//        dog.lookHome();
//    }
//
//    // 测试猫类
//    public static void useAnimal(Cat cat) {
//        cat.eat();
//        cat.catchMouse();
//    }
//
//    // 测试猪类
//    public static void useAnimal(Pig pig) {
//        pig.eat();
//        pig.sleep();
//    }
}

// 父类
 abstract class Animal {
    public abstract void eat();

    public void drink() {
        System.out.println("喝水....");
    }
}

// 子类
class Dog extends Animal {
    @Override
    public void eat() {
        System.out.println("狗吃肉!");
    }

    public void lookHome() {
        System.out.println("看家...");
    }
}

// 子类
class Cat extends Animal {

    @Override
    public void eat() {
        System.out.println("猫吃鱼...");
    }

    public void catchMouse() {
        System.out.println("猫抓老鼠...");
    }
}

// 子类
class Pig extends Animal {

    @Override
    public void eat() {
        System.out.println("猪拱白菜...");
    }

    public void sleep() {
        System.out.println("猪特能睡...");
    }
}
// 输出 :狗吃肉!
猫吃鱼...
猪拱白菜...

多态的转型

如何解决上述多态的缺点呢?就需要多态转型。

  1. 向上转型(upcasting)

父类的引用指向子类对象(把子类类型的对象赋值给父类类型)

Animal an = new Dog();

  1. 向下转型(downcasting)

向上转型我很好理解,但是向下转型稍稍抽象一点,不过细细分析也不难。

首先要知道一点:父类引用指向子类对象,而子类引用不能指向父类对象。(举个很俗的例子,街上看到一个狗,你可以叫动物,但是看到一个动物,你就不能说是狗)。

因此Dog dog = a;这种写法会报错,因为Dog是子类,a是父类,这就需要强制转换。

正确写法:Dog dog = (Dog) a;

注意 :

1 想要调用子类特有的功能 , 需要向下转型

2 对象的本质类型 , 和转型的类型不匹配就会发生 classCastException:类型转换异常

注意 : 方法的参数是一个类 , 那么调用此方法需要传入此类的对象或者此类的子类对象 !!!

于是上面的主方法快就可以改进为:

public class Test02 {
    public static void main(String[] args) {
        useAnimal(new Dog());
        useAnimal(new Cat());
        useAnimal(new Pig());
    }
public static void useAnimal(Animal a) {
    a.eat();
    Dog dog = (Dog) a;
    dog.lookHome();

    Cat cat = (Cat) a;
    cat.catchMouse();

    // 简化写法
    ((Pig)a).sleep();
}
}

但是你以为仅仅通过向下转型就能解决Java不能调用子类特殊方法的问题了?还不行,程序仍然会报错。原因在于当我们执行useAniaml(new Dog())方法时,传入的是一个Dog类对象,因此a的本质是一个“狗”,那么a.eat( )方法以及 dog.lookHome();会被成功执行,但是执行到cat.catchMouse();会报错classCastException(类型转换异常)。顾名思义,Dog类的对象不能强转为Cat类的对象,怎么办呢?这个时候就需要引出instanceof关键字。

instanceof关键字

格式:对象名 instanceof 引用数据类型

作用: 判断一个对象是否属于另一种引用数据类型, 返回boolean类型结果

于是最终的主方法代码改进如下:

public class Test1 {
    public static void main(String[] args) {
        useAnimal(new Dog());
        System.out.println("================");
        useAnimal(new Cat());
        System.out.println("================");
        useAnimal(new Pig());
    }

    public static void useAnimal(Animal a) {
        a.eat();

        // 子类特有功能
        // 向下转型
        if (a instanceof Dog) {
            Dog dog = (Dog) a;
            dog.lookHome();
        }

        if(a instanceof Cat){
            Cat cat = (Cat) a;
            cat.catchMouse();
        }

        if(a instanceof Pig){
            ((Pig) a).sleep();
        }

    }

接口实现多态

多态的应用:

  • 以多态形式创建一个对象。
  • 方法的参数返回值是一个类或者一个接口,那么我们可以传入子类的对象。
  • 方法的返回值类型是一个类或者一个接口,那么我们可以返回子类的对象。
public class Test2 {
    public static void main(String[] args) {
        // 父类类型接收子类类型对象
        Player player = new Mp3Player();
        // 方法的参数是一个父类类型,这里的父类就是Player,那么可以接受子类类型对象
        usePlayer(new Mp3Player());
    }

    public static void usePlayer(Player player) {

    }
    public static Player getPlayer(){
        // 方法的返回值类型是一个父类型,那么可以返回此类型的子类对象
        return new Mp3Player();
    }
}

interface Player {
    void play();
}
class Mp3Player implements Player{
    @Override
    public void play(){
        System.out.println("mp3播放音乐");
    }
}