系列中的上一篇
当前教程
抽象方法和类
这是本系列的最后一篇!

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

抽象方法和类

 

抽象方法和类

抽象类是声明为abstract的类 - 它可能包含也可能不包含抽象方法。抽象类不能被实例化,但可以被子类化。

抽象方法是声明没有实现(没有大括号,后面跟着分号)的方法,如下所示

abstract void moveTo(double deltaX, double deltaY);

如果类包含抽象方法,那么类本身必须声明为abstract,如下所示

public abstract class GraphicObject {
   // declare fields
   // declare nonabstract methods
   abstract void draw();
}

当抽象类被子类化时,子类通常为其父类中的所有抽象方法提供实现。但是,如果它没有这样做,那么子类也必须声明为abstract

注意:接口中的方法(参见接口部分)如果没有声明为默认或静态,则隐式为抽象方法,因此抽象修饰符不用于接口方法。(可以使用,但没有必要。)

 

抽象类与接口的比较

抽象类类似于接口。您不能实例化它们,并且它们可能包含声明有或没有实现的方法的混合。但是,对于抽象类,您可以声明非静态和非最终字段,并定义publicprotectedprivate具体方法。对于接口,所有字段自动为publicstaticfinal,并且您声明或定义(作为默认方法)的所有方法都为public。此外,您只能扩展一个类,无论它是否为抽象类,而您可以实现任意数量的接口。

您应该使用抽象类还是接口?

  • 如果您的情况符合以下任何一项,请考虑使用抽象类

    • 您希望在几个密切相关的类之间共享代码。
    • 您预计扩展抽象类的类具有许多共同方法或字段,或者需要除 public 之外的访问修饰符(例如protectedprivate)。
    • 您希望声明非静态或非最终字段。这使您能够定义可以访问和修改它们所属对象的 state 的方法。
  • 如果您的情况符合以下任何一项,请考虑使用接口

    • 您预计不相关的类将实现您的接口。例如,接口ComparableCloneable由许多不相关的类实现。
    • 您希望指定特定数据类型的行为,但不关心谁实现其行为。
    • 您希望利用类型的多重继承。

JDK 中抽象类的示例是AbstractMap,它是集合框架的一部分。它的子类(包括HashMapTreeMapConcurrentHashMap)共享许多方法(包括get()put()isEmpty()containsKey()containsValue()),这些方法由AbstractMap定义。

JDK 中实现多个接口的类的示例是HashMap,它实现了接口SerializableCloneableMap<K, V>。通过阅读此接口列表,您可以推断出HashMap的实例(无论开发人员或公司是谁实现的类)都可以克隆,是可序列化的(这意味着它可以转换为字节流;参见可序列化对象部分),并且具有 map 的功能。此外,Map<K, V>接口已通过许多默认方法(例如merge()forEach())进行了增强,这些方法不需要以前实现此接口的类定义。

请注意,许多软件库同时使用抽象类和接口;HashMap类实现多个接口,并且还扩展了抽象类AbstractMap

 

抽象类示例

在面向对象的绘图应用程序中,您可以绘制圆形、矩形、线条、贝塞尔曲线以及许多其他图形对象。这些对象都具有某些状态(例如:位置、方向、线条颜色、填充颜色)和行为(例如:移动到、旋转、调整大小、绘制)的共同点。其中一些状态和行为对于所有图形对象都是相同的(例如:位置、填充颜色和移动到)。其他需要不同的实现(例如,调整大小或绘制)。

所有 GraphicObjects 都必须能够绘制或调整自身大小;它们只是在如何做到这一点方面有所不同。这非常适合抽象超类。您可以利用这些相似之处,并声明所有图形对象都从同一个抽象父对象继承,例如GraphicObject

首先,您声明一个抽象类GraphicObject,以提供所有子类完全共享的成员变量和方法,例如当前位置和moveTo()方法。GraphicObject还声明了抽象方法,用于需要由所有子类实现但必须以不同方式实现的方法,例如draw()resize()GraphicObject类可能看起来像这样

abstract class GraphicObject {
    int x, y;
    ...
    void moveTo(int newX, int newY) {
        ...
    }
    abstract void draw();
    abstract void resize();
}

GraphicObject的每个非抽象子类(例如CircleRectangle)都必须为draw()resize()方法提供实现

class Circle extends GraphicObject {
    void draw() {
        ...
    }
    void resize() {
        ...
    }
}
class Rectangle extends GraphicObject {
    void draw() {
        ...
    }
    void resize() {
        ...
    }
}

 

当抽象类实现接口时

在接口部分,我们注意到实现接口的类必须实现接口的所有方法。但是,可以定义一个不实现接口所有方法的类,前提是该类声明为abstract。例如,

abstract class X implements Y {
  // implements all but one method of Y
}

class XX extends X {
  // implements the remaining method in Y
}

在这种情况下,类X必须为抽象类,因为它没有完全实现Y,但类XX实际上实现了Y

 

类成员

抽象类可以具有static字段和static方法。您可以使用类引用(例如,AbstractClass.staticMethod())使用这些static成员,就像使用任何其他类一样。


最后更新: 2021年9月14日


系列中的上一篇
当前教程
抽象方法和类
这是本系列的最后一篇!

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