In all our musical instruments (instrument) examples, the methods within the underlying class instrument are definitely "pseudo" methods. If you call these methods, an error occurs. That's because instrument's intention is to create a generic interface for all classes derived from it.
The only reason to build this universal interface is because it can make different representations of different subtypes. It creates a basic form that allows us to define something that is "generic" in all derived classes. Another way to illustrate this idea is to refer to instrument as an "abstract base class" ("Abstract class"). If you want to handle a series of classes through this generic interface, you need to create an abstract class. For all derived class methods that match the signature of the underlying class declaration, can be invoked through a dynamic binding mechanism (however, as noted in the previous section, if the method name is the same as the underlying class, but the argument or parameter is different, the overload phenomenon may not be what we want).
If there is an abstract class like instrument, the object of that class is almost certainly meaningless. In other words, the role of instrument is simply to express the interface, rather than express some specific implementation details. So it makes no sense to create a instrument object, and we should generally prohibit users from doing that. For this purpose, you can make all the methods in instrument display an error message. But doing so delays the information to the runtime and requires thorough, reliable testing on the user side. In any case, the best approach is to catch the problem during compilation.
To address this problem, Java provides a special mechanism called "abstract methods." It belongs to an incomplete method that contains only one declaration and no method body. The following syntax is used for abstract method declarations:
abstract void X ();
A class that contains an abstract method is called an "abstract class". If a class contains one or more abstract methods, the class must be specified as abstract. Otherwise, the compiler will report an error message to us.
If an abstract class is incomplete, what will the compiler do if someone tries to generate an object of that class? Because it is not safe to create an object belonging to an abstract class, an error message is obtained from the compiler. In this way, the compiler guarantees the "purity" of the abstract class, and we don't have to worry about misusing it.
If you inherit from an abstract class, and you want to generate an object of a new type, you must provide a method definition for all abstract methods in the underlying class. If you do not (you can choose not to do), the derived class will also be abstract, and the compiler will force us to use the abstract keyword to mark the "abstract" nature of that class.
Even if you do not include any abstract methods, you can declare a class as an "abstract class." This ability can be useful if a class does not necessarily have any abstract methods, and we want to ban all instances of that class.
The instrument class can be easily converted into an abstract class. Only part of the method becomes an abstract method, since making a class abstract does not force us to turn all its methods into abstractions at the same time. Here's what it looks like:
Here is an example of the "orchestral" instrument we have modified, which uses abstract classes and methods:
: Music4.java//Abstract classes and methods import java.util.*;
Abstract class Instrument4 {int i;//storage allocated for all public abstract void play ();
Public String What () {return "Instrument4";
public abstract void adjust ();
Class Wind4 extends Instrument4 {public void play () {System.out.println ("Wind4.play ()");
Public String What () {return "Wind4";} public void adjust () {}} class Percussion4 extends Instrument4 {public void play () {System.out.println ("Percussi
On4.play () ");
Public String What () {return "Percussion4";} public void adjust () {}} class Stringed4 extends Instrument4 {public void play () {System.out.println ("Stringed4.
Play () ");
Public String What () {return "Stringed4";}
public void adjust () {}} class Brass4 extends Wind4 {public void play () {System.out.println ("Brass4.play ()");
public void adjust () {System.out.println ("brass4.adjust ()"); } Class Woodwind4 exTends Wind4 {public void play () {System.out.println ("Woodwind4.play ()");
Public String What () {return ' Woodwind4 ';}} public class Music4 {//doesn ' t care about type, so new types//Added to the system still work right:static void
Tune (Instrument4 i) {//... i.play ();
} static void Tuneall (instrument4[] e) {for (int i = 0; i < e.length; i++) tune (e[i));
public static void Main (string[] args) {instrument4[] Orchestra = new INSTRUMENT4[5];
int i = 0;
upcasting during addition to the array:orchestra[i++] = new Wind4 ();
orchestra[i++] = new Percussion4 ();
orchestra[i++] = new Stringed4 ();
orchestra[i++] = new BRASS4 ();
orchestra[i++] = new Woodwind4 ();
Tuneall (orchestra); }
} ///:~
As you can see, in addition to the underlying class, nothing has actually changed.
Creating abstract classes and methods is sometimes useful to us because they make the abstraction of a class an obvious fact that tells the user and the compiler exactly how they intend to use it.