每天学一点之多态真不难

多态polymorphism

1、 案例引入

定义一个Animal类

public class Animal {
    //String name = "animal";
    public void eat(){
        System.out.println("动物吃饭");
    }
}

定义一个Dog类

public class Dog extends Animal {//继承Animal类
    //String name = "dog";
    @Override
    public void eat() {
        System.out.println("狗啃骨头");
    }

}

定义一个Person类

public class Person {
    //喂狗吃饭
   public void feed(Dog dog){
        dog.eat();
    }
}

测试类

public class Test1 {
    public static void main(String[] args) {
        Person p=new Person();
        //测试人喂狗吃饭
        p.feed(new Dog());
    }
}

这时如果需要拓展功能,增加一个猫Cat类,让人喂Cat吃饭,应该如何做?

很简单,在Person类中增加喂猫的方法,那如果后期还要不断的增加新功能(新增各种动物类),就要不断增加Person类对应的方法。这里就出现了一个问题:违背了软件开发中的开闭原则(对修改关闭,对扩展开放),并且大量类同代码冗余,即每次新增的方法除了参数类型不同,其他都基本相同。

能不能用简单方式解决?可以。

只需要在Person类中定义一个类似的方法,参数为Animal,其他全舍去,这就是多态的应用。

//修改后的Person类
public class Person {
    //喂动物吃饭
   public void feed(Animal animal){
        animal.eat();
    }
}
2、什么是多态
  1. 多态的形式
    父类类型 变量名 = 子类对象;

父类类型:指子类继承的父类类型,或者实现的父接口类型。
所以说继承是多态的前提

//多态的引用形式
Animal a = new Cat();//Cat对象也属于其父类Animal类型。猫也是一个动物。

  1. 多态的表现
    多态引用形式的具体表现:编译时类型与运行时类型不一致,编译时看左边的“父类”,父类必须包含要运行的方法,运行时看右边的“子类”。
    也就是我们常说的:编译看左边,运行看右边。
public class Test {
    public static void main(String[] args) {
        // 多态形式,创建对象
        Animal a = new Cat();  
        // 编译时,a变量呈现Animal类型特征,即Animal类中有eat方法,a调用eat方法时编译才能成功
        a.eat();//运行时,实际执行的是Cat类中重写的eat方法。 
        
        //a.catchMouse();//错误,编译时左边父类Animal中没有此方法,所以编译失败

        // 多态形式,创建对象
        a = new Dog(); 
        //运行时执行的是 Dog类中重写的eat方法
        a.eat();               
    }  
}
  1. 多态的好处
    运行时,看“子类”,如果子类重写了方法,一定是执行子类重写的方法;变量引用的子类对象不同,执行的方法就不同,实现动态绑定。代码编写更灵活、功能更强大,可维护性和扩展性更好了。
  • 提高程序的扩展性
  • 降低类与类之间的耦合度
3、多态的应用形式

1、多态应用在成员变量和方法参数

  • 方法的形参是父类类型,调用方法的实参是子类对象
  • 实例变量声明父类类型,实际存储的是子类对象
    public void adopt(Pet pet) {//形参是父类类型,实参是子类对象
        this.pet = pet;
    }
    public void feed(){
        pet.eat();//pet实际引用的对象类型不同,执行的eat方法也不同
    }
        Dog dog = new Dog();
        person.adopt(dog);//实参是dog子类对象,形参是父类Pet类型
        person.feed();

        Cat cat = new Cat();
        person.adopt(cat);//实参是cat子类对象,形参是父类Pet类型
        person.feed();

2、多态应用在数组
数组元素类型声明为父类类型,实际存储的是子类对象

 private Pet[] pets;//数组元素类型是父类类型,元素存储的是子类对象
  for (int i = 0; i < pets.length; i++) {
            pets[i].eat();//pets[i]实际引用的对象类型不同,执行的eat方法也不同          
        }
        pets[0] = new Dog();//多态引用
        pets[1] = new Cat();//多态引用

3、多态应用在方法的返回值类型

//返回值类型是父类类型,实际返回的是子类对象
    public Pet sale(String type){
        switch (type){
            case "Dog":
                return new Dog();
            case "Cat":
                return new Cat();
        }
        return null;
    }
4、向上转型与向下转型

1、类型转换

向上转型:当左边的变量的类型(父类) > 右边对象/变量的类型(子类),我们就称为向上转型

  • 此时,编译时按照左边变量的类型处理,就只能调用父类中有的变量和方法,不能调用子类特有的变量和方法了
  • 但是,运行时,仍然是对象本身的类型,所以执行的方法是子类重写的方法体。
  • 此时,一定是安全的,而且也是自动完成的

向下转型:当左边的变量的类型(子类)<右边对象/变量的编译时类型(父类),我们就称为向下转型

  • 此时,编译时按照左边变量的类型处理,就可以调用子类特有的变量和方法了
  • 但是,运行时,仍然是对象本身的类型
  • 不是所有通过编译的向下转型都是正确的,可能会发生ClassCastException,为了安全,可以通过isInstanceof关键字进行判断

2、如何向上转型与向下转型
向上转型:自动完成

向下转型:(子类类型)父类变量

 //没有类型转换
        Dog dog = new Dog();//dog的编译时类型和运行时类型都是Dog

        //向上转型
        Pet pet = new Dog();//pet的编译时类型是Pet,运行时类型是Dog
        pet.setNickname("小白");
        pet.eat();//可以调用父类Pet有声明的方法eat,但执行的是子类重写的eat方法体
//        pet.watchHouse();//不能调用父类没有的方法watchHouse

        Dog d = (Dog) pet;
        d.eat();//可以调用eat方法
        d.watchHouse();//可以调用子类扩展的方法watchHouse

        Cat c = (Cat) pet;//编译通过,因为从语法检查来说,pet的编译时类型是Pet,Cat是Pet的子类,所以向下转型语法正确
        //这句代码运行报错ClassCastException,因为pet变量的运行时类型是Dog,Dog和Cat之间是没有继承关系的

3、instanceof关键字
instanceof关键字用于判断一个对象的运行时类型

为了避免ClassCastException的发生,Java提供了 instanceof 关键字,给引用变量做类型的校验,只要用instanceof判断返回true的,那么强转为该类型就一定是安全的,不会报ClassCastException异常。

格式:
变量/匿名对象 instanceof 数据类型

那么,哪些instanceof判断会返回true呢?

  • 变量/匿名对象的编译时类型 与 instanceof后面数据类型是直系亲属关系才可以比较
  • 变量/匿名对象的运行时类型<= instanceof后面数据类型,才为true
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

努力奋斗的JAVA小余

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值