系列中的上一篇
当前教程
多态
系列中的下一篇

系列中的上一篇: 重写和隐藏方法

系列中的下一篇: 对象作为超类

多态

 

多态

多态的词典定义指的是生物学中的一种原理,即生物体或物种可以具有多种不同的形式或阶段。这一原理也可以应用于面向对象编程和像 Java 语言这样的语言。类的子类可以定义自己的独特行为,同时共享父类的一些相同功能。

可以通过对 Bicycle 类进行一些小的修改来演示多态。例如,可以向该类添加一个 printDescription() 方法,该方法显示当前存储在实例中的所有数据。

public void printDescription(){
    System.out.println("\nBike is " + "in gear " + this.gear
        + " with a cadence of " + this.cadence +
        " and travelling at a speed of " + this.speed + ". ");
}

为了演示 Java 语言中的多态特性,用 MountainBikeRoadBike 类扩展 Bicycle 类。对于 MountainBike,添加一个用于悬挂的字段,该字段是一个 String 值,表示自行车是否具有前减震器,Front。或者,自行车具有前后减震器,Dual

以下是更新后的类

public class MountainBike extends Bicycle {
    private String suspension;

    public MountainBike(
               int startCadence,
               int startSpeed,
               int startGear,
               String suspensionType){
        super(startCadence,
              startSpeed,
              startGear);
        this.setSuspension(suspensionType);
    }

    public String getSuspension(){
      return this.suspension;
    }

    public void setSuspension(String suspensionType) {
        this.suspension = suspensionType;
    }

    public void printDescription() {
        super.printDescription();
        System.out.println("The " + "MountainBike has a" +
            getSuspension() + " suspension.");
    }
} 

请注意重写的 printDescription() 方法。除了之前提供的信息之外,还将有关悬挂的附加数据包含在输出中。

接下来,创建 RoadBike 类。由于公路自行车或赛车自行车具有细轮胎,因此添加一个属性来跟踪轮胎宽度。以下是 RoadBike

public class RoadBike extends Bicycle{
    // In millimeters (mm)
    private int tireWidth;

    public RoadBike(int startCadence,
                    int startSpeed,
                    int startGear,
                    int newTireWidth){
        super(startCadence,
              startSpeed,
              startGear);
        this.setTireWidth(newTireWidth);
    }

    public int getTireWidth(){
      return this.tireWidth;
    }

    public void setTireWidth(int newTireWidth){
        this.tireWidth = newTireWidth;
    }

    public void printDescription(){
        super.printDescription();
        System.out.println("The RoadBike" + " has " + getTireWidth() +
            " MM tires.");
    }
}

请注意,printDescription() 方法再次被重写。这次,将显示有关轮胎宽度的信息。

总而言之,有三个类:BicycleMountainBikeRoadBike。这两个子类重写了 printDescription() 方法并打印独特的信息。

以下是一个测试程序,它创建三个 Bicycle 变量。每个变量都分配给三个自行车类之一。然后打印每个变量。

public class TestBikes {
  public static void main(String[] args){
    Bicycle bike01, bike02, bike03;

    bike01 = new Bicycle(20, 10, 1);
    bike02 = new MountainBike(20, 10, 5, "Dual");
    bike03 = new RoadBike(40, 20, 8, 23);

    bike01.printDescription();
    bike02.printDescription();
    bike03.printDescription();
  }
}

以下是测试程序的输出

Bike is in gear 1 with a cadence of 20 and travelling at a speed of 10. 

Bike is in gear 5 with a cadence of 20 and travelling at a speed of 10. 
The MountainBike has a Dual suspension.

Bike is in gear 8 with a cadence of 40 and travelling at a speed of 20. 
The RoadBike has 23 MM tires.

Java 虚拟机 (JVM) 会调用与每个变量中引用的对象相对应的适当方法。它不会调用由变量类型定义的方法。这种行为被称为虚拟方法调用,它展示了 Java 语言中重要多态特性的一个方面。

 

隐藏字段

在一个类中,与超类中字段同名的字段会隐藏超类的字段,即使它们的类型不同。在子类中,无法通过其简单名称引用超类中的字段。相反,必须通过 super 访问该字段,这将在下一节中介绍。一般来说,我们不建议隐藏字段,因为它会使代码难以阅读。

 

使用关键字 super

访问超类成员

如果您的方法重写了其超类中的某个方法,则可以通过使用关键字 super 来调用重写的方法。您还可以使用 super 来引用隐藏的字段(尽管不建议隐藏字段)。请考虑以下类 Superclass

public class Superclass {

    public void printMethod() {
        System.out.println("Printed in Superclass.");
    }
}

以下是一个子类,名为 Subclass,它重写了 printMethod()

public class Subclass extends Superclass {

    // overrides printMethod in Superclass
    public void printMethod() {
        super.printMethod();
        System.out.println("Printed in Subclass");
    }
    public static void main(String[] args) {
        Subclass s = new Subclass();
        s.printMethod();    
    }
}

Subclass 中,简单名称 printMethod() 指的是在 Subclass 中声明的名称,它重写了 Superclass 中的名称。因此,要引用从 Superclass 继承的 printMethod()Subclass 必须使用限定名称,使用 super,如所示。编译和执行 Subclass 会打印以下内容

Printed in Superclass.
Printed in Subclass

子类构造函数

以下示例说明了如何使用 super 关键字来调用超类的构造函数。回想一下 Bicycle 示例中 MountainBikeBicycle 的子类。以下是 MountainBike(子类)构造函数,它调用超类构造函数,然后添加自己的初始化代码

public MountainBike(int startHeight, 
                    int startCadence,
                    int startSpeed,
                    int startGear) {
    super(startCadence, startSpeed, startGear);
    seatHeight = startHeight;
} 

调用超类构造函数必须是子类构造函数中的第一行。

调用超类构造函数的语法为

super();

super(parameter list);

使用 super(),将调用超类的无参数构造函数。使用 super(parameter list),将调用具有匹配参数列表的超类构造函数。

注意:如果构造函数没有显式调用超类构造函数,则 Java 编译器会自动插入对超类无参数构造函数的调用。如果超类没有无参数构造函数,您将收到编译时错误。Object 确实有这样的构造函数,因此如果 Object 是唯一的超类,则不会出现问题。

如果子类构造函数显式或隐式调用了其超类的构造函数,您可能会认为会有一系列构造函数被调用,一直追溯到 Object 的构造函数。事实上,情况确实如此。这被称为构造函数链,在存在很长的类继承链时,您需要了解它。

 

编写 final 类和方法

您可以声明一个类中的一些或所有方法为 final。您在方法声明中使用 final 关键字来指示该方法不能被子类重写。Object 类就是这样做的——它的许多方法都是 final 的。

如果某个方法的实现不应该更改,并且它对对象的持续状态至关重要,您可能希望将其设为 final。例如,您可能希望将此 ChessAlgorithm 类中的 getFirstPlayer() 方法设为 final

class ChessAlgorithm {
    enum ChessPlayer { WHITE, BLACK }
    ...
    final ChessPlayer getFirstPlayer() {
        return ChessPlayer.WHITE;
    }
    ...
}

从构造函数调用的方法通常应该声明为 final。如果构造函数调用非 final 方法,则子类可能会重新定义该方法,从而导致意外或不希望的结果。

请注意,您还可以声明整个类为 final。声明为 final 的类不能被子类化。这在创建不可变类(例如 String 类)时特别有用。


最后更新时间: 2021 年 9 月 14 日


系列中的上一篇
当前教程
多态
系列中的下一篇

系列中的上一篇: 重写和隐藏方法

系列中的下一篇: 对象作为超类