使用抽象方法有什么意义?

时间:2011-12-06 01:32:36

标签: java abstract-class

使用“抽象方法”有什么意义?抽象类无法实例化,但抽象方法呢?他们只是在这里说“你必须实现我”,如果我们忘记它们,编译器会抛出错误吗?

这是否意味着别的什么?我还读到了一些关于“我们不必重写相同代码”的内容,但在抽象类中,我们只“声明”抽象方法,因此我们必须重写子类中的代码。

你能帮我理解一下吗?我检查了关于“抽象类/方法”的其他主题,但我没有找到答案。

7 个答案:

答案 0 :(得分:25)

假设您有三台打印机需要为LexmarkCanonHP编写驱动程序。

所有三台打印机都有print()getSystemResource()方法。

但是,每台打印机只有print()不同。 getSystemResource()在三台打印机中保持不变。您还有另一个问题,您想应用多态。

因为getSystemResource()对于所有三台打印机都是相同的,所以这可以推送到超类来实现,在Java中,这可以通过在超类中进行抽象来完成,并且在超类中创建方法抽象,类本身也需要是抽象的。

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();
}

答案 1 :(得分:15)

除了提示你必须实现它之外,最大的好处是任何通过抽象类类型引用该对象的人(包括抽象类本身中的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;
    }
}

如果没有这种抽象方法,基类就无法说“以某种方式处理这种状态”。但是,我们不希望在基类中提供默认实现,因为它并不意味着什么 - 如何定义“其他人将实现此”的默认实现?

请注意,皮肤猫的方法不止一种。 strategy pattern将涉及声明一个声明accumulate模式的接口,并将该接口的实例传递给不再抽象的基类。在术语方面,那是使用组合而不是继承(你已经用两个对象,一个聚合器和一个加法器组成了一个加法聚合器)。

答案 2 :(得分:2)

抽象方法仅定义派生类必须实现的契约。这就是你如何确保他们实际上永远存在的方式。

所以让我们举一个抽象类Shape。它应该有一个抽象方法draw(),它应该绘制它。 (Shape是抽象的,因为我们不知道如何绘制一般形状)通过在draw中使用抽象方法Shape,我们保证所有派生的类别,实际上可以绘制,示例Circle执行draw。稍后如果我们忘记在某个类中实现draw,这是从Shape派生的,编译器实际上会帮助提供错误。

答案 3 :(得分:1)

简单地说,声明一个类是“抽象”的,你正在为继承它的子类强制执行“契约”,因此它提供了维护“契约”的好方法。

答案 4 :(得分:0)

如果所有抽象都是声明抽象方法,那么你是正确的,它有点傻,界面可能会更优越。

但是,抽象类通常会实现一些(甚至可能是全部)方法,只留下一些抽象类。例如,AbstractTableModel。这样可以节省大量代码的重写。

接口的另一个“优点”是抽象类可以声明要使用的子类的字段。因此,如果您非常确定任何合理的实现都有一个名为uniqueID的String,您可以在抽象类中声明它,加上相关的getter / setter,稍后会保存一些类型。

答案 5 :(得分:0)

抽象类是包含一个或多个抽象方法的类。抽象方法是已声明但不包含任何实现的方法。抽象类可能无法实例化,并且需要子类为抽象方法提供实现。让我们看一个抽象类的示例和一个抽象方法。

假设我们通过创建一个以基类为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!");
 }
}

请注意,abstract关键字用于表示抽象方法和抽象类。现在,任何要实例化的动物(如狗或牛)都必须实现makeNoise方法-否则就无法创建该类的实例。让我们看一下扩展Animal类的Dog and Cow子类。

现在您可能想知道为什么不将抽象类声明为接口,而让Dog and Cow实现该接口。当然可以,但是您还需要实现饮食方法。通过使用抽象类,您可以继承其他(非抽象)方法的实现。您不能使用接口来做到这一点-接口不能提供任何方法实现。

简单地说,接口应包含所有抽象方法,但不包含方法的实现,或者我们不能在接口中定义非抽象方法,在接口中,所有方法应为抽象,但在抽象类中,我们可以定义抽象和非抽象方法,因此对于定义非抽象方法,我们不必定义另一个类来实现同一对象的行为,这是抽象类优于接口的优点。

答案 6 :(得分:-2)

抽象方法必须被任何不抽象的子类覆盖。

因此,例如,您定义了一个抽象类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)
    }
  }
}