Abstract classes
and methods
In all the instrument examples, the methods in the base class Instrument were always dummy methods. If these methods are ever called, youve done something wrong. Thats because the intent of Instrument is to create a common interface for all the classes derived from it.
The only reason to establish this common interface is so it can be expressed differently for each different subtype. It establishes a basic form, so you can say whats in common with all the derived classes. Another way of saying this is to call Instrument an abstract base class (or simply an abstract class). You create an abstract class when you want to manipulate a set of classes through this common interface. All derived-class methods that match the signature of the base-class declaration will be called using the dynamic binding mechanism. (However, as seen in the last section, if the methods name is the same as the base class but the arguments are different, youve got overloading, which probably isnt what you want.)
If you have an abstract class like Instrument, objects of that class almost always have no meaning. That is, Instrument is meant to express only the interface, and not a particular implementation, so creating an Instrument object makes no sense, and youll probably want to prevent the user from doing it. This can be accomplished by making all the methods in Instrument print error messages, but that delays the information until run time and requires reliable exhaustive testing on the users part. Its better to catch problems at compile time.
Java provides a mechanism for doing this called the abstract method.[32] This is a method that is incomplete; it has only a declaration and no method body. Here is the syntax for an abstract method declaration:
abstract void f();
A class containing abstract methods is called an abstract class. If a class contains one or more abstract methods, the class itself must be qualified as abstract. (Otherwise, the compiler gives you an error message.)
If an abstract class is incomplete, what is the compiler supposed to do when someone tries to make an object of that class? It cannot safely create an object of an abstract class, so you get an error message from the compiler. This way, the compiler ensures the purity of the abstract class, and you dont need to worry about misusing it.
If you inherit from an abstract class and you want to make objects of the new type, you must provide method definitions for all the abstract methods in the base class. If you dont (and you may choose not to), then the derived class is also abstract, and the compiler will force you to qualify that class with the abstract keyword.
Its possible to create a class as abstract without including any abstract methods. This is useful when youve got a class in which it doesnt make sense to have any abstract methods, and yet you want to prevent any instances of that class.
The Instrument class can easily be turned into an abstract class. Only some of the methods will be abstract, since making a class abstract doesnt force you to make all the methods abstract. Heres what it looks like:

Heres the orchestra example modified to use abstract classes and methods:
//: 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"
});
}
} ///:~
You can see that theres really no change except in the base class.
Its helpful to create abstract classes and methods because they make the abstractness of a class explicit, and tell both the user and the compiler how it was intended to be used.