4.8 Other Modifiers for Classes

Modifiers abstract and final can be applied to top-level and nested classes.

abstract Classes

A class can be specified with the keyword abstract to indicate that it cannot be instantiated. A class might choose to do this if the abstraction it represents is so general that it needs to be specialized in order to be of practical use. A class Vehicle might be specified as abstract to represent the general abstraction of a vehicle, as creating instances of the class would not make much sense. Creating instances of non-abstract subclasses, like Car and Bus, would make more sense, as this would make the abstraction more concrete.

A class that has one or more abstract methods (see Section 4.10, p. 147) must be declared abstract. Obviously such classes cannot be instantiated, as their implementation is only partial. A class might choose this strategy to dictate certain behavior, but allow its subclasses the freedom to provide the relevant implementation. In other words, subclasses of the abstract class have to take a stand and provide implementations of inherited abstract methods before they can be instantiated. A subclass that does not provide an implementation of its inherited abstract methods must also be declared abstract.

In Example 4.8, the definition of abstract class Light has an abstract method kwhPrice at (1). This forces its subclasses to provide the implementation for this method. The subclass TubeLight provides an implementation for the method kwhPrice at (2). The class Factory creates an instance of class TubeLight at (3). Reference variables of an abstract class can be declared, as shown at (4), but an abstract class cannot be instantiated, as shown at (5). References of an abstract class can denote objects of the subclasses, as shown at (6).

Example 4.8 Abstract Classes
abstract class Light {
    // Fields
    int     noOfWatts;       // wattage
    boolean indicator;       // on or off
    String  location;        // placement

    // Instance methods
    public void switchOn()  { indicator = true; }
    public void switchOff() { indicator = false; }
    public boolean isOn()   { return indicator; }

    // Abstract Instance Method
    abstract public double kwhPrice();               // (1) No method body
}
class TubeLight extends Light {
    // Fields
    int tubeLength;

    // Implementation of inherited abstract method.
    public double kwhPrice() { return 2.75; }        // (2)
}

public class Factory {
    public static void main(String[] args) {
        TubeLight cellarLight = new TubeLight();     // (3) OK
        Light nightLight;                            // (4) OK
    //  Light tableLight = new Light();              // (5) Compile time error
        nightLight = cellarLight;                    // (6) OK
        System.out.println("KWH price: " + nightLight.kwhPrice());
    }
}

Output from the program:

KWH price: 2.75

Interfaces just specify the method prototypes and not any implementation; they are, by their nature, implicitly abstract (i.e., they cannot be instantiated). Though it is legal, it is redundant to declare an interface with the keyword abstract.

final Classes

A class can be declared final to indicate that it cannot be extended; that is, one cannot declare subclasses of a final class. This implies that one cannot override any methods declared in such a class. In other words, the class behavior cannot be changed by subclassing. The class marks the lower boundary of its implementation inheritance hierarchy. Only a class whose definition is complete (i.e., has implementations of all its methods) can be declared final.

A final class must be complete, whereas an abstract class is considered incomplete. Classes, therefore, cannot be both final and abstract at the same time. Interfaces, which are inherently abstract, thus cannot be declared final. A final class and an interface represent two extremes when it comes to providing implementation. An abstract class represents a compromise between these two extremes.

The Java API includes many final classes; for example, java.lang.String which cannot be specialized any further by subclassing.

If it is decided that the class TubeLight in Example 4.8 cannot, or should not, be extended, it can be declared final:

final class TubeLight extends Light {
    // Fields
    int tubeLength;

    // Implementation of inherited abstract method.
    public double kwhPrice() { return 2.75; }
}

Discussion of final methods, fields and local variables can be found in Section 4.10, p. 146.

Table 4.3. Summary of Other Modifiers for Classes and Interfaces

Modifiers

Classes

Interfaces

abstract

Class may contain abstract methods and thus, cannot be instantiated.

Implied.

final

The class cannot be extended (i.e., it cannot be subclassed).

Not possible.