使用“抽象方法”的目的是什么?抽象类无法实例化,但是抽象方法呢?它们只是用来声明“你必须实现我”,如果我们忘记实现它们,编译器就会报错吗?
这是否意味着还有其他含义?我也读到过“我们不必重写相同的代码”,但在抽象类中,我们只是“声明”了抽象方法,因此我们仍需在子类中重写代码。
您能帮助我更好地理解吗?我查阅了关于“抽象类/方法”的其他主题,但没有找到答案。
使用“抽象方法”的目的是什么?抽象类无法实例化,但是抽象方法呢?它们只是用来声明“你必须实现我”,如果我们忘记实现它们,编译器就会报错吗?
这是否意味着还有其他含义?我也读到过“我们不必重写相同的代码”,但在抽象类中,我们只是“声明”了抽象方法,因此我们仍需在子类中重写代码。
您能帮助我更好地理解吗?我查阅了关于“抽象类/方法”的其他主题,但没有找到答案。
假设你需要为三个打印机编写驱动程序,分别是 Lexmark
、Canon
和 HP
。
这三个打印机都有 print()
和 getSystemResource()
方法。
然而,每个打印机的 print()
方法都不同,而 getSystemResource()
方法对于所有三个打印机都相同。另外,你希望应用多态性。
由于 getSystemResource()
对于所有三个打印机都相同,因此可以将其上推到超类中实现,并让子类实现 print()
方法。在Java中,这可以通过在超类中使 print()
方法抽象化来完成。注意:当在一个类中将方法抽象化时,该类本身也需要抽象化。
public abstract class Printer{
public void getSystemResource(){
// real implementation of getting system resources
}
public abstract void print();
}
public class Canon extends Printer{
public void print(){
// here you will provide the implementation of print pertaining to Canon
}
}
public class HP extends Printer{
public void print(){
// here you will provide the implementation of print pertaining to HP
}
}
public class Lexmark extends Printer{
public void print(){
// here you will provide the implementation of print pertaining to Lexmark
}
}
注意,HP、Canon和Lexmark类没有提供getSystemResource()
的实现。
最后,在你的主类中,你可以这样做:
public static void main(String args[]){
Printer printer = new HP();
printer.getSystemResource();
printer.print();
}
Interface
的作用,对吗? - Neerkoli除了提醒你必须实现它之外,另一个重要的优点是任何按其抽象类类型引用对象的人(包括在抽象类本身中使用this
)都可以使用该方法。
例如,假设我们有一个负责获取状态并以某种方式进行操作的类。 抽象类将负责获取输入,将其转换为long
(例如),并以某种方式将该值与先前的值组合在一起——这个“某种方式”就是抽象方法。 抽象类可能如下所示:
public abstract class StateAccumulator {
protected abstract long accumulate(long oldState, long newState);
public handleInput(SomeInputObject input) {
long inputLong = input.getLong();
state = accumulate(state, inputLong);
}
private long state = SOME_INITIAL_STATE;
}
现在您可以定义一个加法累加器:
public class AdditionAccumulator extends StateAccumulator {
@Override
protected long accumulate(long oldState, long newState) {
return oldState + newState;
}
}
如果没有这个抽象方法,基类就无法说“以某种方式处理这个状态”。但我们不想在基类中提供默认实现,因为这并没有什么意义——如何为“其他人将实现此内容”定义默认实现呢?
请注意,有多种方法可以解决问题。 策略模式 将涉及声明一个接口,该接口声明了accumulate
模式,并将该接口的实例传递给不再是抽象的基类。按照行话的说法,这是使用组合而非继承(您已将两个对象(聚合器和加法器)组合成一个加法聚合器)。
抽象类是包含一个或多个抽象方法的类。抽象方法是声明但不包含实现的方法。抽象类不能被实例化,要求子类提供抽象方法的实现。让我们看一个抽象类和一个抽象方法的例子。
假设我们正在通过创建以Animal为基类的类层次结构来对动物的行为进行建模。动物可以做各种事情,如飞行、挖掘和行走,但也有一些共同的操作,如进食、睡觉和发出声音。所有动物都会执行一些共同的操作,但它们的方式却不尽相同。当某个操作的方式不同时,这就是抽象方法的好候选对象(强制子类提供自定义实现)。让我们看一个非常原始的Animal基类,它定义了一个用于发出声音的抽象方法(如狗叫、牛哞哞、猪哼哼等)。
public abstract Animal {
public void sleep{
// sleeping time
}
public void eat(food)
{
//eat something
}
public abstract void makeNoise();
}
public Dog extends Animal {
public void makeNoise() {
System.out.println("Bark! Bark!");
}
}
public Cow extends Animal {
public void makeNoise() {
System.out.println("Moo! Moo!");
}
}
抽象方法仅定义派生类必须实现的合同。这是确保它们始终如一的方式。
例如,让我们看一个抽象类 Shape
。它将具有一个抽象方法 draw()
来绘制它。( Shape
是抽象的,因为我们不知道如何绘制通用形状)通过在 Shape
中具有 抽象方法draw
,我们保证所有可以被绘制的派生类都实现了draw
,例如 Circle
。之后,如果我们忘记在从 Shape
派生的某个类中实现draw
,编译器将会给出错误提示。
简单来说,将一个类声明为“抽象”,你正在强制要求继承它的子类遵守一个“合同”,因此它为维护这个“合同”提供了一种好的方式。
如果一个抽象类只是声明了抽象方法,那么你是正确的,这有点傻,使用接口可能更好。
但通常一个抽象类会实现一些(甚至全部)方法,只留下少数方法作为抽象方法。例如,AbstractTableModel。这样可以节省大量重写代码的时间。
另一个比接口更好的“优势”是,抽象类可以为子类声明字段以供使用。因此,如果您相信任何合理的实现都会有一个名为uniqueID的字符串,您可以在抽象类中声明它,以及相关的getter/setter,以后就可以节省一些打字时间。
抽象方法必须被任何不是抽象的子类重写。
例如,您定义了一个抽象类Log,并强制子类重写该方法:
public abstract class Log{
public void logError(String msg){
this.log(msg,1)
}
public void logSuccess(String msg){
this.log(msg,2)
}
public abstract void log(String msg,int level){}
}
public class ConsoleLog{
public void log(String msg,int level){
if(level=1){
System.err.println(msg)
}else{
System.out.println(msg)
}
}
}