Java provides the reserved word extends for specifying a hierarchical relationship between two classes.
For example, suppose you have a Vehicle class and want to introduce Car and Truck classes that extend Vehicle. Listing 2-21 uses extends to cement these relationships.
Listing 2-21. Relating classes via extends class Vehicle
{
// member declarations }
class Car extends Vehicle {
// member declarations }
class Truck extends Vehicle {
// Member declarations }
Listing 2-21 codifies relationships that are known as “is-a” relationships: a car or a truck is a kind of vehicle. In this relationship, Vehicle is known as the base class, parent class, or superclass; and each of Car and Truck is known as the derived class, child class, or subclass.
■ Caution You cannot extend a final
class. For example, if you declared
Vehicleas
final class Vehicle, the compiler would report an error upon encountering
class Car extends Vehicleor
class Truck extends Vehicle. Developers declare their classes
finalwhen they do not want these classes to be extended (for security or other reasons).
As well as being capable of providing its own member declarations, each of Car and Truck is capable of inheriting member declarations from its Vehicle superclass. As Listing 2-22 shows, non-private inherited members become accessible to members of the Car and Truck classes.
Listing 2-22. Inheriting members class Vehicle
{
private String make;
private String model;
private int year;
Vehicle(String make, String model, int year) {
this.make = make;
this.model = model;
this.year = year;
CHAPTER 2 DISCOVERING CLASSES AND OBJECTS
Car(String make, String model, int year, int numWheels) {
super(make, model, year);
this.numWheels = numWheels;
}
public static void main(String[] args) {
Car car = new Car("Toyota", "Camry", 2011, 4);
System.out.println("Make = "+car.getMake());
System.out.println("Model = "+car.getModel());
System.out.println("Year = "+car.getYear());
System.out.println("Number of wheels = "+car.numWheels);
System.out.println();
car = new Car("Aptera Motors", "Aptera 2e/2h", 2012, 3);
System.out.println("Make = "+car.getMake());
System.out.println("Model = "+car.getModel());
System.out.println("Year = "+car.getYear());
System.out.println("Number of wheels = "+car.numWheels);
} }
class Truck extends Vehicle {
private boolean isExtendedCab;
Truck(String make, String model, int year, boolean isExtendedCab) {
super(make, model, year);
this.isExtendedCab = isExtendedCab;
}
public static void main(String[] args) {
Truck truck = new Truck("Chevrolet", "Silverado", 2011, true);
System.out.println("Make = "+truck.getMake());
System.out.println("Model = "+truck.getModel());
System.out.println("Year = "+truck.getYear());
System.out.println("Extended cab = "+truck.isExtendedCab);
CHAPTER 2 DISCOVERING CLASSES AND OBJECTS
} }
Listing 2-22’s Vehicle class declares private fields that store a vehicle’s make, model, and year; a constructor that initializes these fields to passed arguments; and getter methods that retrieve these fields’ values.
The Car subclass provides a private numWheels field, a constructor that initializes a Car object’s Vehicle and Car layers, and a main() class method for testing this class. Similarly, the Truck subclass provides a private isExtendedCab field, a constructor that initializes a Truck object’s Vehicle and Truck layers, and a main() class method for testing this class.
Car’s and Truck’s constructors use reserved word super to call Vehicle’s constructor with Vehicle-oriented arguments, and then initialize Car’s numWheels and Truck’s isExtendedCab instance fields, respectively. The super() call is analogous to specifying this() to call another constructor in the same class, but invokes a superclass constructor instead.
■ Caution The super()
call can only appear in a constructor. Furthermore, it must be the first code that is specified in the constructor. If
super()is not specified, and if the superclass does not have a noargument constructor, the compiler will report an error because the subclass constructor must call a noargument superclass constructor when
super()is not present.
Car’s main() method creates two Car objects, initializing each object to a specific make, model, year, and number of wheels. Four System.out.println() method calls subsequently output each object’s information. Similarly, Truck’s main() method creates a single Truck object, and also initializes this object to a specific make, model, year, and flag (Boolean true/false value) indicating that the truck is an extended cab. The first three System.out.println() method calls retrieve their pieces of information by calling a Car or Truck instance’s inherited getMake(), getModel(), and getYear() methods.
The final System.out.println() method call directly accesses the instance’s numWheels or
isExtendedCab instance field. Although it’s generally not a good idea to access an instance field directly (because it violates information hiding), each of the Car and Truck class’s main() methods, which provides this access, is present only to test these classes and would not exist in a real application that uses these classes.
Assuming that Listing 2-22 is stored in a file named Vehicle.java, execute javac Vehicle.java to compile this source code into Vehicle.class, Car.class, and Truck.class classfiles. Then execute java Car to test the Car class. This execution results in the following output:
Make = Toyota Model = Camry Year = 2011
Number of wheels = 4 Make = Aptera Motors Model = Aptera 2e/2h Year = 2012
Number of wheels = 3
Continuing, execute java Truck to test the Truck class. This execution results in the following output:
CHAPTER 2 DISCOVERING CLASSES AND OBJECTS
Make = Chevrolet Model = Silverado Year = 2011
Extended cab = true
■ Note A class whose instances cannot be modified is known as an immutable class. Vehicle
is an example. If
Car’s and
Truck’s
main()methods, which can directly read/write
numWheelsor
isExtendedCab, were not present,
Carand
Truckwould also be examples of immutable classes. Also, a class cannot inherit constructors, nor can it inherit private fields and methods. For example,
Cardoes not inherit
Vehicle’s constructor, nor does it inherit
Vehicle’s private
make,
model, and
yearfields.
A subclass can override (replace) an inherited method so that the subclass’s version of the method is called instead. Listing 2-23 shows you that the overriding method must specify the same name,
parameter list, and return type as the method being overridden.
Listing 2-23. Overriding a method class Vehicle
{
private String make;
private String model;
private int year;
Vehicle(String make, String model, int year) {
this.make = make;
this.model = model;
this.year = year;
}
void describe() {
System.out.println(year+" "+make+" "+model);
} }
class Car extends Vehicle {
private int numWheels;
Car(String make, String model, int year, int numWheels) {
super(make, model, year);
}
void describe() {
System.out.print("This car is a "); // Print without newline – see Chapter 1.
super.describe();
}
public static void main(String[] args)
CHAPTER 2 DISCOVERING CLASSES AND OBJECTS
{
Car car = new Car("Ford", "Fiesta", 2009, 4);
car.describe();
} }
Listing 2-23’s Car class declares a describe() method that overrides Vehicle’s describe() method to output a car-oriented description. This method uses reserved word super to call Vehicle’s describe() method via super.describe();.
■ Note Call a superclass method from the overriding subclass method by prefixing the method’s name with
reserved word
superand the member access operator. If you don’t do this, you end up recursively calling the subclass’s overriding method. Use
superand the member access operator to access non-
privatesuperclass fields from subclasses that mask these fields by declaring same-named fields.
If you were to compile Listing 2-23 (javac Vehicle.java) and run the Car application (java Car), you would discover that Car’s overriding describe() method executes instead of Vehicle’s overridden describe() method, and outputs This car is a 2009 Ford Fiesta.
■ Caution You cannot override a final