一、多态的优势
传智教育徐磊老师 曾经总结过这一点:多态的优势一共有两点
- 在多态形式下,右边对象可以实现解耦合,便于扩展和维护
这句话比较拗口,举个栗子:比如现在用多态方式创建对象,并调用调用方法
Person p = new Student();
// 业务逻辑发生改变时,后续代码无需修改
p.work();
例如过了几天,我不想让学生去工作了,而是想让老师 Teacher 去工作,此时在代码中,我们只需要将创建对象的代码修改即可,后续的代码其实是不需要修改了,更重要的是,p.work
运行的其实是修改之后的 Teacher类
中的方法。
这个就是多态的第一个优势,它可以让我们的程序更加利于维护。
- 定义方法的时候,如果使用父类作为形参,那么这个方法可以接收所有子类对象,体现多态的扩展性与便利。
这个 好处 在刚刚我们也写了代码去演示。
当我们以后在调用一个方法的时候,发现一个方法的形参是一个类名,那么我们就可以传递这个类所有的子类对象。
二、优势的示例代码
1)案例1:StringBuilder
我们来看一个以前写过的东西
StringBuilder sb= new StringBuilder();
sb.append()
当我们想要将一些数据添加到 sb
当中,就需要调用 append
方法。
在调用的时候发现,有一个方法,类型是 Object
。在之前我们说过,Object
是Java中的顶级父类,所有的类都 直接
/ 间接
继承 Object类
。
这里 append
方法的形参是 Object
,就表示我们可以把 任意的对象
都可以往里面放。
如果没有多态,要往 StringBuilder
中添加数据的话,每种类型都要去写一个方法,太麻烦了。
但是现在有了多态了,所有的对象都可以统一的使用这个方法即可,因为它的参数类型是 Object
。
2)案例2:ArrayList
ArrayList(集合)其实也是一个容器,也可以往这个容器里面去添加元素。
我在创建的时候,后面需要加一个泛型,泛型表示元素的类型。
例如下面泛型是 String
,表示它只能把字符串类型的往里面添加,其他的就不能添加了。
ArrayList<String> list = new ArrayList<>();
但假如集合后面不写泛型呢?
不写泛型就表示现在对添加到集合里面的元素没有任意的限定,即所有的类型都可以往里面添加。
ArrayList list = new ArrayList();
list.add()
如下图,如果没有写泛型,add
方法的形参是 Object
类型。
一旦是 Objcet
,就表示任意类型。
三、多态的弊端
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
类型。
最后回车即可。
一旦转化为子类类型后,再用 d
去调用,子类的特有功能 lookHome()
也就出来了。
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.Cat
:Dog类
不能被转成 Cat类
。
at com.itheima.a03polymorphismdemo3.Test.main(Test.java:34)
:表示这个错误在 main方法
的第 22 行。
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
关键字来进行一个判断即可。