抽象类和抽象方法

在所有乐器的例子中,基类 Instrument 中的方法往往是“哑(dummy)”方法。若要调
用这些方法,就会出现一些错误。这是因为 Instrument 类的目的是为它的所有导出类创
建一个通用接口。


建立这个通用接口的唯一原因是,不同的子类可以用不同的方式表示此接口。它建立起一个
基本形式,用来表示所有导出类的共同部分。另一种说法是将 Instrument 类称作“抽象
基类”(或简称抽象类)。当我们想通过这个通用接口操纵一系列类时,就需创建一个抽象
类。与任何基类所声明的签名相符的导出类方法,都会通过动态绑定机制来调用。(然而,
 






正如前一节所讲,如果方法名与基类中的相同,但是参数不同,就会出现重载,这或许不是
我们想要的)


如果我们只有一个像 Instrument 这样的抽象类,那么该类的对象几乎没有任何意义。也
就是说,Instrument 只是表示了一个接口,没有具体的实现内容;因此,创建一个
Instrument 对象没有什么意义,并且我们可能还想阻止使用者这样做。通过在 Instrument
的所有方法中打印出错误信息,就可以实现这个目的。但是这样做会将错误信息延迟到运行
期才可获得,并且需要在客户端进行可靠、详尽的测试。所以最好是在编译期间捕获这些问
题。


为此,Java提供一个叫做“抽象方法(abstract method)1”的机制。这种方法是不完整
的;仅有声明而没有方法体。下面是抽象方法声明所采用的语法:


abstract void f();


包含抽象方法的类叫做“抽象类(abstract class)”。如果一个类包含一个或多个抽象方法,
该类必须被限制为是抽象的。(否则,编译器就会报错)


如果一个抽象类不完整,那么当我们试图产生该类的对象时,编译器会怎样处理呢?由于为
一个抽象类创建对象是不安全的,所以我们会从编译器那里得到一条出错信息。这里,编译
器会确保抽象类的纯粹性,我们不必担心会误用它。


如果从一个抽象类继承,并想创建该新类的对象,那么我们就必须为基类中的所有抽象方法
提供方法定义。如果不这样做(可以选择不做),那么导出类便也是抽象类,且编译器将会
强制我们用 abstract 关键字来限制修饰这个类。


我们也可能会创建一个没有任何抽象方法的抽象类。考虑这种情况:如果我们有一个类,让
其包含任何 abstract 方法都显得没有实际意义,但是我们却想要阻止产生这个类的任何
对象,那么这时这样做就很有用了。


Instrument 类可以很容易地转化成抽象类。既然使某个类成为抽象类并不需要所有的方

法都是抽象的,所以仅需将某些方法声明为抽象的即可。

下面是修改过的“管弦乐队”的例子,其中采用了抽象类和抽象方法:


//: c07:music4:Music4.java
// Abstract classes and methods.
package c07.music4; 
import com.bruceeckel.simpletest.*; 
import java.util.*; 
import c07.music.Note;


abstract class Instrument { 
private int i; // Storage allocated for each
public abstract void play(Note n);
public String what() { 
return "Instrument";
  }
public abstract void adjust(); 
}


class Wind extends Instrument { 
public void play(Note n) {
 






    System.out.println("Wind.play() " + n); 
  }
public String what() { return "Wind"; }
public void adjust() {}
}


class Percussion extends Instrument {
public void play(Note n) {
    System.out.println("Percussion.play() " + n); 
  }
public String what() { return "Percussion"; }
public void adjust() {}
}


class Stringed extends Instrument {
public void play(Note n) {
    System.out.println("Stringed.play() " + n); 
  }
public String what() { return "Stringed"; } 
public void adjust() {}
}


class Brass extends Wind { 
public void play(Note n) {
    System.out.println("Brass.play() " + n);
  }
public void adjust() {
    System.out.println("Brass.adjust()");
  }
}


class Woodwind extends Wind {
public void play(Note n) {
    System.out.println("Woodwind.play() " + n); 
  }
public String what() { return "Woodwind"; } 
}


public class Music4 {
private static Test monitor = new Test(); 
// Doesn't care about type, so new types
// added to the system still work right:
static void tune(Instrument i) { 
// ...
 






    i.play(Note.MIDDLE_C); 
  }
static void tuneAll(Instrument[] e) { 
for(int i = 0; i < e.length; i++) 
      tune(e[i]);
  }
public static void main(String[] args) { 
// Upcasting during addition to the array:
    Instrument[] orchestra = { 
new Wind(), 
new Percussion(), 
new Stringed(),
new Brass(), 
new Woodwind() 
    };
    tuneAll(orchestra); 
    monitor.expect(new String[] { 
"Wind.play() Middle C",
"Percussion.play() Middle C",
"Stringed.play() Middle C",
"Brass.play() Middle C",
"Woodwind.play() Middle C"
    });
  }
} ///:~


我们可以看出,除了基类,实际上并没有什么改变。


创建抽象类和抽象方法非常有用,因为它们可以显化一个类的抽象性,并告诉用户和编译器
怎样按照它所预期的方式来使用。


你可能感兴趣的:(抽象类和抽象方法)