抽象方法和类
抽象方法和类
抽象类是声明为abstract
的类 - 它可能包含也可能不包含抽象方法。抽象类不能被实例化,但可以被子类化。
抽象方法是声明没有实现(没有大括号,后面跟着分号)的方法,如下所示
abstract void moveTo(double deltaX, double deltaY);
如果类包含抽象方法,那么类本身必须声明为abstract
,如下所示
public abstract class GraphicObject {
// declare fields
// declare nonabstract methods
abstract void draw();
}
当抽象类被子类化时,子类通常为其父类中的所有抽象方法提供实现。但是,如果它没有这样做,那么子类也必须声明为abstract
。
注意:接口中的方法(参见接口部分)如果没有声明为默认或静态,则隐式为抽象方法,因此抽象修饰符不用于接口方法。(可以使用,但没有必要。)
抽象类与接口的比较
抽象类类似于接口。您不能实例化它们,并且它们可能包含声明有或没有实现的方法的混合。但是,对于抽象类,您可以声明非静态和非最终字段,并定义public
、protected
和private
具体方法。对于接口,所有字段自动为public
、static
和final
,并且您声明或定义(作为默认方法)的所有方法都为public
。此外,您只能扩展一个类,无论它是否为抽象类,而您可以实现任意数量的接口。
您应该使用抽象类还是接口?
如果您的情况符合以下任何一项,请考虑使用抽象类
- 您希望在几个密切相关的类之间共享代码。
- 您预计扩展抽象类的类具有许多共同方法或字段,或者需要除 public 之外的访问修饰符(例如
protected
和private
)。 - 您希望声明非静态或非最终字段。这使您能够定义可以访问和修改它们所属对象的 state 的方法。
如果您的情况符合以下任何一项,请考虑使用接口
- 您预计不相关的类将实现您的接口。例如,接口
Comparable
和Cloneable
由许多不相关的类实现。 - 您希望指定特定数据类型的行为,但不关心谁实现其行为。
- 您希望利用类型的多重继承。
- 您预计不相关的类将实现您的接口。例如,接口
JDK 中抽象类的示例是AbstractMap
,它是集合框架的一部分。它的子类(包括HashMap
、TreeMap
和ConcurrentHashMap
)共享许多方法(包括get()
、put()
、isEmpty()
、containsKey()
和containsValue()
),这些方法由AbstractMap
定义。
JDK 中实现多个接口的类的示例是HashMap
,它实现了接口Serializable
、Cloneable
和Map<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
的每个非抽象子类(例如Circle
和Rectangle
)都必须为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日