【Java】多态的优势和弊端

一、多态的优势

传智教育徐磊老师 曾经总结过这一点:多态的优势一共有两点

  • 在多态形式下,右边对象可以实现解耦合,便于扩展和维护

这句话比较拗口,举个栗子:比如现在用多态方式创建对象,并调用调用方法

Person p = new Student();
// 业务逻辑发生改变时,后续代码无需修改
p.work(); 

例如过了几天,我不想让学生去工作了,而是想让老师 Teacher 去工作,此时在代码中,我们只需要将创建对象的代码修改即可,后续的代码其实是不需要修改了,更重要的是,p.work 运行的其实是修改之后的 Teacher类 中的方法。

image-20240417080812683

这个就是多态的第一个优势,它可以让我们的程序更加利于维护。

  • 定义方法的时候,如果使用父类作为形参,那么这个方法可以接收所有子类对象,体现多态的扩展性与便利。

这个 好处 在刚刚我们也写了代码去演示。

当我们以后在调用一个方法的时候,发现一个方法的形参是一个类名,那么我们就可以传递这个类所有的子类对象。


二、优势的示例代码

1)案例1:StringBuilder

我们来看一个以前写过的东西

StringBuilder sb= new StringBuilder();
sb.append()

当我们想要将一些数据添加到 sb 当中,就需要调用 append 方法。

在调用的时候发现,有一个方法,类型是 Object 。在之前我们说过,Object 是Java中的顶级父类,所有的类都 直接 / 间接 继承 Object类

这里 append 方法的形参是 Object,就表示我们可以把 任意的对象 都可以往里面放。

如果没有多态,要往 StringBuilder 中添加数据的话,每种类型都要去写一个方法,太麻烦了。

但是现在有了多态了,所有的对象都可以统一的使用这个方法即可,因为它的参数类型是 Object

image-20240417081433813


2)案例2:ArrayList

ArrayList(集合)其实也是一个容器,也可以往这个容器里面去添加元素。

我在创建的时候,后面需要加一个泛型,泛型表示元素的类型。

例如下面泛型是 String ,表示它只能把字符串类型的往里面添加,其他的就不能添加了。

ArrayList<String> list = new ArrayList<>();

但假如集合后面不写泛型呢?

不写泛型就表示现在对添加到集合里面的元素没有任意的限定,即所有的类型都可以往里面添加。

ArrayList list = new ArrayList();
list.add()

如下图,如果没有写泛型,add 方法的形参是 Object 类型。

一旦是 Objcet ,就表示任意类型。

image-20240417090050724


三、多态的弊端

1)代码示例

package com.itheima.a03polymorphismdemo3;


public class Test {
    public static void main(String[] args) {
        //创建对象
        Animal a = new Dog();
        //编译看左边,运行看右边
        a.eat();
    }
}

class Animal{
    public void eat(){
        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("猫抓老鼠");
    }
}

多态的弊端:不能调用子类的特有功能(例如Dog类 中的 lookHome()Cat类 中的 catchMouse()。)

//当我使用 `a` 变量去调用 lookHome() 方法时,发现IDEA根本没有 lookHome() 的提示,并且如果强行加上,也会报错!
//报错的原因?
//当调用成员方法的时候,编译看左边,运行看右边
//那么在编译的时候会先检查左边的父类中有没有这个方法,如果没有直接报错。
//因此在这它发现了,父类中没有 lookHome() 方法,这个时候就会直接报错!
a.lookHome();

解决方案:把调用者 a 变回子类类型就可以了,即强制转换(如果要把一个取值范围大的数值,赋值给取值范围小的变量。是不允许直接赋值的。如果一定要这么做就需要加入强制转换。)

就算忘记怎么强转为子类类型也没关系,可以使用 IDEA 的提示。

点击下面的红色波浪线,alt + enter 选择第一个:Cast:转换,整句话的意思是:将它转换成 com.itheima.a03polymorphismdemo3 包中的 Dog 类型。

最后回车即可。

image-20240417091900406

一旦转化为子类类型后,再用 d 去调用,子类的特有功能 lookHome() 也就出来了。

image-20240417092158419


2)细节

转换的时候不能瞎转,如果转成其他类的类型,就会报错。

//例如上面创建对象的时候:Animal a = new Dog();,子类使用的是狗的类型,但是在强转的时候我将它强制为 Cat类。
//然后用c去调用Cat类特有的catchMouse()方法:
Cat c = (Cat) a;
//编译的时候是不会报错的,但是运行的时候会抛出一个异常
c.catchMouse();

遇到异常不要怕,要学会看异常。

Exception in thread "main":异常在 main方法 中。

mian方法 中出现了一个 java.lang.ClassCastException 异常,翻译过来就是:类型转化异常。

后面的 class com.itheima.a03polymorphismdemo3.Dog cannot be cast to class com.itheima.a03polymorphismdemo3.Cat (com.itheima.a03polymorphismdemo3.Dog and com.itheima.a03polymorphismdemo3.Cat are in unnamed module of loader 'app') at com.itheima.a03polymorphismdemo3.Test.main(Test.java:34) :就是异常的解释说明。

解释说明比较长,但是没关系,我们一个个看。

com.itheima.a03polymorphismdemo3.Dog:是一个包名 + 类名。

class com.itheima.a03polymorphismdemo3.Dog cannot be cast to class com.itheima.a03polymorphismdemo3.CatDog类 不能被转成 Cat类

at com.itheima.a03polymorphismdemo3.Test.main(Test.java:34):表示这个错误在 main方法 的第 22 行。

image-20240417092755409


3)新出现的问题

此时又有新的问题出现了:倘若我也不知道你创建的对象是什么类型,那该怎么办呢?

//此时就可以来做一个if判断,判断a是不是Dog类型 / a是不是Cat类型....
//Java的类型判断是通过一个关键字来判断类型是否相等:instanceof
//instanceof 前面是变量名,后面是类名,相当于就是判断,你这个变量所记录的对象是不是Dog类型,如果是,结果为 true,如果不是,结果为 false
if(a instanceof Dog){
    Dog d = (Dog) a;
    d.lookHome();
}else if(a instanceof Cat){
    Cat c = (Cat) a;
    c.catchMouse();
}else{
    System.out.println("没有这个类型,无法转换");
}

上面的代码看似是解决问题了,但是每次还是需要先判断再强转,太麻烦了!

因此Java在 JDK14 的时候,提出了一个新特性:将判断与强转两个合在一起,写在一行。

直接在 Dog / Cat 的后面写上一个变量名即可。

//新特性
//解释:先判断a是否为Dog类型,如果是,则强转成Dog类型,转换之后变量名为d
//如果不是Dog类型,则不强转,结果直接是false
if(a instanceof Dog d){
    d.lookHome();
}else if(a instanceof Cat c){
    c.catchMouse();
}else{
    System.out.println("没有这个类型,无法转换");
}

四、总结

1、多态的优势

定义方法的时候,如果使用父类作为形参,那么这个方法可以接收所有子类对象,体现多态的扩展性与便利。

2、多态的弊端是什么?

不能使用子类的特有功能。

如果想要强行使用子类的特有功能,可以进行类型转换。

3、引用数据类型的类型转换,有几种方式?

引用类型的类型转换其实分为两种:自动类型转换、强制类型转换。

在刚刚,重点学习的是第二种(强制类型转换)。

这里解释一下自动类型转换:例如下面代码,将创建的子类对象赋值给父类类型的变量,由小变大,这个就是自动类型转换。

Person p = new Student();

强制类型转化:将父类类型的变量,强转为子类类型

Student s = (Student) p;

4、强制类型转换能解决什么问题?

可以转换成真正的子类类型,从而调用子类独有功能。

5、面试官问你:多态中如果想要使用子类的特有功能该怎么办?

首先你要说,在多态中,它是不能使用子类的特有功能的;如果你一定要用,我们要做强转。

并且在强转的时候,它有可能会发生 对象的类型与现在要转的类型不一致,此时就会抛出异常。

解决办法就是:转换的时候用 instanceof 关键字来进行一个判断即可。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值