有些时候,由于有多个子类同时继承于同一个父类,而此时需要对这些子类进行一些统一的操作。如果我们分别对每个子类都设计同一个方法,就会使代码非常冗长和复杂。所以此时我们可以通过父类,对这些子类进行一个统一的操作。

基本概念

多态指的是同类型的对象表现出的不同形态的特征。具体来说,是指同一操作作用于不同的对象可以有不同的解释,并产生不同的执行效果。在java中,多态是指把类中具有相同功能的不同方法使用同一个方法名实现。

格式

父类类型 对象名称 = 子类对象

例子:假设乐高城市组(legoCity)、乐高漫威(legoMarvel)都是乐高(Lego)的子类,我们想打印他们的价格,那我们可以如下:

public static void register(Lego l){
    l.showPrice();
}

如果l对应的子类对象是乐高城市组,那么打印的就是乐高城市组的价格,另外一个也同理。

补充:在java中,引用变量有两种类型,声明类型和实际类型。

声明类型:变量声明时被指定的类型

实际类型:被变量引用的类型

比如 Lego l = new legoCity(),Lego类就是声明类型,而LegoCity就是实际类型。

前提

调用多态有下面三个前提:

  • 多态之间有继承关系
  • 有父类引用指向子类对象
  • 有方法的重写

多态调用成员变量与成员方法的特点

在下面的代码中,我们定义了一个Lego父类和两个子类,两个子类都进行了方法的重写。

class Lego{
    public void Greet(){
        String name="Lego";
        System.out.println("你想买乐高吗?");
    }
}

class legoCity extends Lego{
    String name="legoCity";
    @Override
    public void Greet(){
        System.out.println("你想买乐高城市组吗?");
    }
}

class legoMarvel extends Lego{
    String name="legoMarvel";
    @Override
    public void Greet(){
        System.out.println("你想买乐高漫威吗?");
    }
}

之后我们在main测试类中调用成员变量:

public class Test{
    public static void main(){
        Lego l=new legoCity();
        System.out.println(l.name);
    }
}

输出结果:Lego

这说明,在多态中调用成员变量,其结果调用的是“=”左边的父类的成员变量。

我们接着在main测试类中调用成员方法:

public class Test{
    public static void main(){
        Lego l=new legoCity();
        l.Greet();
    }
}

输出结果:你想买乐高城市组吗?

这说明,在多态中调用成员方法,其结果调用的是“=”右边子类的成员方法。

那么究竟是什么原理呢?

我们可以这样理解:在子类的对象中,会将父类的成员变量也继承下来。而Lego l表示lLego类型的,所以会优先去在Lego这个类中去找。而成员方法由于被子类重写,所以会被子类的方法覆盖,所以按照子类中的方法去运行。

多态的优缺点

优点:

  • 多态使用父类作为参数,可以接收所有子类对象,具有扩展性和便利性
  • 方便维护

缺点:

  • 不能调用子类的特有功能

这个缺点如何补救呢?其实原理很简单。在数据类型中我们可以通过强制转换来改变数据类型,而这里也同理,我们可以强制将父类转化成子类:

Lego l=new legoCity();
legoCity lc=(legoCity)l;
//错误写法:
legoMarvel lc=(legoMarvel)l;

这就引入了对象的类型装换。

对象的类型转换

概念:对象的类型转换指的是可以将一个对象的类型转换成继承结构中的另一个类型。分为两种情况:向上转型和向下转型.

现在我们假设legoCity是父类Lego的一个子类。

class Lego{
    public void Greet(){
        String name="Lego";
        System.out.println("你想买乐高吗?");
    }
}

class legoCity extends Lego{
    String name="legoCity";
    @Override
    public void Greet(){
        System.out.println("你想买乐高城市组吗?");
    }
}

class legoCityPolicestation extends legoCity{
    String name="legoCityPolicestation";
    @Override
    public void Greet(){
        System.out.println("你想买乐高城市组警察局吗?");
    }
}
  • 向上转型(隐式转换):是从子类到父类的转换
Lego l=new legoCity();
legoCity lc=l;
  • 向下转型(显示转换):是从父类到子类的转换
Lego l=new legoCity();
legoCityPolicestation lc=(legoCity)l;

但是这样会发生报错(报错原因略微有点绕此处不阐述)。

针对这种情况,java提供了instanceof关键字,用于判断一个对象是否是一个类或者接口的实例,表达式返回值为boolean类型。

格式:

变量名 instanceof 类名

修改后的代码如下:

Lego l=new legoCityPolicestation();
if(l instanceof legoCity){
    legoCity lc=(legoCity)l;
    代码1;
}
else if(l instanceof legoCityPolicestation){
    legoCityPolicestation lc=(legoCityPolicestation)l;
    代码2;
}

如果这个实例是legoCity类型,那么就会执行代码1;如果是legoCityPolicestation类型,那么就会执行代码2。