Chapter 12: Inheritance, Interfaces and Polymorphism
In the mid-1960s, two researchers at the Norwegian Computing Center in Oslo, Ole-Johan Dahl and Kristen Nygaard, were writing programs that imitated real systems: ships arriving at a port, people forming queues, machines breaking down and being repaired. The programs kept growing awkward. A ferry, a cargo ship and a fishing boat all had a name, a position and a speed, and all of them moved. Yet each also carried something the others did not. Writing three separate descriptions meant writing the same movement code three times — and correcting a mistake in it three times.
Their answer, published in the language Simula 67, was to allow one class to be declared as a special kind of another. The shared description was written once. Each particular kind then said, in effect, “I am a vessel, and in addition I have this.” When the shared description was corrected, every kind built on it was corrected along with it.
That single idea — one class extending another — is the idea Java took up, and it is the subject of this chapter. It arrives with three questions its designers had to settle, and so will you: what a subclass is allowed to touch, what it is allowed to change, and how a running program decides which version of a method to carry out.
The shape such a program takes is drawn in Fig. 12.0-1.
Diagram
Fig. 12.0-1 – one box labelled “Vessel” at the top listing the attributes name, position and speed and the method move(); three boxes below it labelled “Ferry”, “CargoShip” and “FishingBoat”, each listing one attribute of its own (passengers, tonnage, netSize); an upward arrow from each of the three boxes to the Vessel box, every arrow labelled “extends”
By the end of this chapter you will be able to answer each of these questions.
12.1 Inheritance: Extending a Class Instead of Rewriting It
A class, as you learnt in Class XI, is a description: it states what data an object of that kind holds and what that object can do. So far every class you have written has been a description standing on its own. Nothing connected one class to another.
Real descriptions are rarely so separate. A cash memo and a delivery note are both bills. A savings account and a current account are both accounts. In each pair the two things share a great deal and differ in a little. A programming language that can only write descriptions from scratch forces you to repeat the shared part, once for every kind.
Java lets you say instead that one class is a particular kind of another. The shared description is written once, in one class, and the specialised class states only what it adds or alters. That mechanism is called inheritance, and this section builds it from the problem upwards.
12.1.1 The Problem That Inheritance Solves
Consider the register of a school library. Every item on the shelves — a book, a magazine, an atlas — is given an accession number when it is bought, and every item has a title. Whatever the item, the librarian must be able to display these two details.
Suppose you write one class for books and another for magazines. The book class needs a title, an accession number, an author and a method to display the details. The magazine class needs a title, an accession number, the month of the issue and a method to display the details. Two of the three variables, and almost all of the display code, are identical in the two classes.
That duplication is drawn in Fig. 12.1-1. It is not merely untidy; it is dangerous. Suppose the school later decides that every displayed record must also show the shelf on which the item stands. You must now change both classes. If you change one and forget the other, the program still compiles and still runs, and the mistake shows up only when somebody notices that magazines are printing one line less than books.
Diagram
Fig. 12.1-1 – two boxes side by side, “class Book” on the left and “class Magazine” on the right; each box lists the variables title and accNo and the method showItem(), and these three shaded to mark them as repeated; below the shading each box lists one unshaded member of its own — author in Book, month in Magazine; a caption arrow between the two shaded regions labelled “written twice, corrected twice”
The problem, stated generally, is this: two descriptions overlap, and the language gives you no way to write the overlap once. That is exactly the gap inheritance fills.
12.1.2 Superclass and Derived Class: the extends Keyword
The method of repair is straightforward. Pull the shared part out into a class of its own — call it LibraryItem — and then declare the class Book as a particular kind of LibraryItem. The keyword that states this relationship is extends.
class Book extends LibraryItem
{
}That one line does a great deal. Every non-private variable and every non-private method of LibraryItem becomes available inside Book and on every object of Book, without being written again.
The class whose description is acquired is the superclass; it is also called the base class or parent class. The class that acquires it is the derived class; it is also called the subclass or child class. In the declaration above, LibraryItem is the superclass and Book is the derived class. Note the direction carefully: the arrow of inheritance runs from the derived class upward to the superclass, because it is the derived class that names the other, never the reverse.
| Feature | Superclass (base class) | Derived class (subclass) |
|---|---|---|
| What it holds | the description common to a whole group of kinds | one specialised kind within that group |
| How it is written | declared on its own, naming no other class | declared with extends, naming its superclass |
| Access to the other’s members | cannot use the members added by the derived class | can use the non-private members of its superclass |
| How many | one class may serve as superclass to any number of classes | one class may extend exactly one superclass |
| Objects | an object of it holds only its own members | an object of it holds the inherited members and the added ones together |
The last row is the one students most often get backwards. A Book object is not a small object holding only an author; it holds a title and an accession number as well, because a book is a library item. Only when this is clear does the program below read correctly.
12.1.3 What a Derived Class Receives and What It Adds
It helps to picture a Book object as being built in two layers, as in Fig. 12.1-2: the layer described by the superclass, and the layer added by the derived class. The object is one object, not two; the layers only record where each member’s description was written.
Diagram
Fig. 12.1-2 – a single rounded box labelled “one Book object”; inside it an upper compartment labelled “inherited from LibraryItem” containing title and accNo, and a lower compartment labelled “added by Book” containing author; to the right, a list of the methods callable on the object — setItem(), showItem(), setAuthor(), showBook() — with the first two tagged “inherited”
Three things pass from superclass to derived class, and three do not. A derived class inherits the superclass’s instance variables and its member methods, and through them the behaviour they define. It does not inherit the superclass’s constructors, it cannot directly reach the superclass’s private members, and it gains no power to alter the superclass itself. The first of these is taken up in Section 12.3 and the second in Section 12.2.
Within the derived class you are free to add. You may declare new instance variables that the superclass never had, as Book adds author. You may declare new methods, as Book adds setAuthor() and showBook(). And you may declare a method with the same name as one in the superclass, which redefines it — the subject of Section 12.4.
Why this matters: reusability
The practical gain from all this is reusability: code written and tested once is used by many classes without being copied. Three consequences follow, and they are worth stating separately.
First, the program models a real relationship rather than merely storing data. The sentence “a book is a library item” is not a comment; it is a line of Java, and the compiler enforces it.
Second, a correction made in the superclass reaches every derived class automatically. If showItem() is improved, every kind of item improves with it, and no class can be left behind by an oversight.
Third, inheritance is transitive. If ReferenceBook extends Book and Book extends LibraryItem, then a ReferenceBook object holds the members of all three, and title is as available in ReferenceBook as it is in Book.
An honest limitation belongs here too. Inheritance ties the derived class tightly to the superclass: any change in the superclass’s members ripples down to every class beneath it, and a careless change can break code the author of the superclass has never seen. Depth is therefore not a virtue. A hierarchy two or three levels deep is usually easier to reason about than one of eight.
12.1.4 Forms of an Inheritance Hierarchy
Once a class may extend a class, several shapes of hierarchy become possible. Java permits some of them and rules out one. The three that Java supports directly are drawn in Fig. 12.1-3.
Diagram
Fig. 12.1-3 – three panels side by side, each of plain boxes joined by upward arrows labelled “extends”. Panel (a) “single”: Book above which sits LibraryItem, one arrow. Panel (b) “hierarchical”: LibraryItem at the top with Book, Magazine and Atlas below it, three arrows converging upward on LibraryItem. Panel (c) “multilevel”: LibraryItem at the top, Book below it, ReferenceBook below Book, two arrows forming a chain
In single inheritance, one derived class extends one superclass. This is the form used in the program of Section 12.1.2 and it is the form all the others are built from.
In hierarchical inheritance, several derived classes extend the same superclass. The library register is naturally of this shape: books, magazines and atlases are all library items, and each writes down only what makes it different.
class LibraryItem
{
String title;
int accNo;
}
class Book extends LibraryItem
{
String author;
}
class Magazine extends LibraryItem
{
String month;
}In multilevel inheritance, a derived class is itself extended, so that the chain runs through more than one level. It is also called nested inheritance. Here ReferenceBook is a kind of Book, and a Book is a kind of LibraryItem, so a ReferenceBook object carries the members of all three classes.
class LibraryItem
{
String title;
int accNo;
}
class Book extends LibraryItem
{
String author;
}
class ReferenceBook extends Book
{
boolean issuable;
}Notice that each link in that chain is itself a single inheritance. Multilevel inheritance is not a new mechanism; it is single inheritance applied twice.
The form Java does not allow
Multiple inheritance is the form in which one derived class extends two or more superclasses at once. Java does not permit it for classes. You may not write class C extends A, B, and the compiler rejects it outright.
The reason is drawn in Fig. 12.1-4. Suppose classes A and B both contain a method show(), and class C were allowed to extend both. What should c.show() mean? There is no honest answer: the language would either have to guess or force the programmer to disambiguate at every use. Rather than carry that complication, Java restricts a class to one superclass and supplies a different mechanism — the interface — for the cases where behaviour must come from more than one source. That mechanism is the subject of Section 12.8.
Diagram
Fig. 12.1-4 – class A on the upper left and class B on the upper right, each box containing the method show(); class C below and between them, with an upward arrow to A and another to B, both arrows drawn as dashed lines and crossed out; a question mark beside class C labelled “which show()?”; the whole panel captioned “not allowed between classes in Java”
Hybrid inheritance is the name given to a structure that combines more than one of these forms — for instance a hierarchical arrangement in which one of the derived classes is itself extended further. In Java a hybrid structure is built freely out of single, hierarchical and multilevel inheritance; only the multiple-inheritance part of it, where two superclasses meet in one class, must be handled with interfaces instead.
12.1.5 Specialisation, Not Mere Reuse
Inheritance is such a convenient way of borrowing code that it is easy to reach for it whenever one class happens to contain a method another class wants. This is a mistake, and it is worth naming clearly.
Write B extends A only when an object of B genuinely is an object of A — when every sentence true of an A remains true of a B. This is called the is-a relationship, and the test is a sentence in ordinary language: “a reference book is a book” is true, so ReferenceBook extends Book is sound.
Now consider a class Barcode that prints an accession number as a machine-readable strip. It would be convenient to write class Barcode extends LibraryItem, because then the accession number is already there. But “a barcode is a library item” is false. A barcode is not catalogued, not issued and not shelved; it merely refers to something that is. Extending here would give Barcode a title, an author and a display method that make no sense for it, and would leave the program asserting something untrue.
What that situation calls for is the has-a relationship: the Barcode class holds a LibraryItem as one of its variables and uses its methods, without pretending to be one. Holding an object of another class is not inheritance at all; it is ordinary composition, and it carries none of inheritance’s obligations.
The distinction matters more than it may seem. A superclass promises that everything it can do, every class below it can do too. Break that promise — by extending a class purely to raid it for a useful method — and every later reader of the program, and every piece of code written against the superclass, is misled. Reuse is a benefit of inheritance; it is not a reason for it.
12.2 Member Access in a Derived Class
Section 12.1 said that a derived class receives the members of its superclass. That statement was deliberately loose. Inheritance does not hand over everything without condition: the author of the superclass decides, member by member, how far each one may travel.
The instrument of that decision is the access specifier — the keyword written in front of a variable or a method that fixes where in the program that member may be used. Java offers four levels of access, and every member of every class has exactly one of them:
private— the most restricted;- no specifier at all, called default access;
protected;public— the least restricted.
Behind these four lies a principle you met in Class XI. Encapsulation is the practice of keeping an object’s data inside the object and letting the outside world reach it only through the object’s own methods, so that the class alone controls how its data may change. An access specifier is how encapsulation is actually enforced, and inheritance does not suspend it. A derived class is closer to the superclass than an ordinary class is, but it is still on the outside of whatever the superclass chose to hide.
The reach of each level is drawn in Fig. 12.2-1, and the rest of this section takes them one at a time.
Diagram
Fig. 12.2-1 – four nested rectangles, drawn one inside the next. Innermost labelled “the class itself”; around it “other classes of the same package”; around that “derived classes in another package”; outermost “every other class, anywhere”. Four labelled arrows on the left, each starting outside and stopping at the boundary it can reach: private stops at the innermost rectangle, default at the second, protected at the third, public at the outermost
The word package in that figure means a named group of classes kept together, in practice the folder they are compiled in. Unless you deliberately create packages, all the classes of one project — and all the classes of every program in this chapter — belong to a single unnamed package, so the second rectangle of Fig. 12.2-1 quietly contains all of them.
12.2.1 Private Members of the Superclass
A member declared private may be used only inside the class in which it is declared. That rule has no exception for inheritance. If a superclass declares a variable private, a derived class cannot read it, cannot assign to it and cannot even name it; if a superclass declares a method private, a derived class cannot call it.
This surprises students, because the private variable is undeniably there inside the object. And it is there: a Fare object created from a class that extends Route does hold the ratePerKm declared inside Route. What the derived class lacks is not the storage but the permission.
12.2.2 Protected Visibility
Between “only my own class” and “anybody at all” lies the case the superclass author most often wants: my own class, and the classes built on it. That is what protected visibility provides.
For an ordinary Class XII project, where every class sits in the same package, the practical effect is simple: a protected member behaves like a public one inside the project, but a class outside the project can reach it only by inheriting.
class Route
{
protected double distance;
}
class Fare extends Route
{
void showDistance()
{
System.out.println("Distance in km: " + distance);
}
}Here distance is used directly inside Fare, with no method call and no super, exactly as if it had been declared in Fare itself. That is the convenience protected buys.
It buys it at a price, and the price should be stated plainly. Every variable you mark protected becomes part of a promise to every future subclass: you may no longer rename it, change its type or stop maintaining it without breaking classes you did not write. A method marked protected is a far safer promise than a variable, because the method’s body can change while the promise stays the same. The general advice, and it is worth taking, is to keep instance variables private and to expose behaviour rather than data.
12.2.3 Public Members and the Access Comparison
A member declared public may be used from anywhere: inside its class, in any other class, in any package, whether the using class is derived from it or not. A derived class therefore inherits public members with no restriction whatever, and so does every other class that can lay hands on an object.
Public members are how a class states what it offers to the world. In a well-built class the public members are almost all methods — the operations the class promises to perform — while the data behind them stays private.
The whole picture is best held in one table.
| Access specifier | Its own class | Another class, same package | Derived class, another package | Unrelated class, another package |
|---|---|---|---|---|
private |
yes | no | no | no |
| default (no specifier) | yes | yes | no | no |
protected |
yes | yes | yes | no |
public |
yes | yes | yes | yes |
Read the table downwards and you will see that each row adds one place to the row above it; the four levels form a ladder, not four unrelated choices. Read the third column alone and you have the rule for inheritance across packages: a derived class inherits usable protected and public members, and nothing else.
Note the second row carefully, because it is often misstated. A member written with no specifier is not public. It is accessible throughout its own package and nowhere else. Since all your classes normally live in one package, such a member will seem to behave publicly — right up to the day somebody puts a subclass in a different package and the code stops compiling.
12.2.4 Reaching a Private Member through Accessor and Mutator Methods
If a superclass keeps its data private, and a derived class genuinely needs that data, the superclass must supply a door. Two small methods do the job, and they are so common that they have names.
Both are declared in the superclass, and both are declared public or protected so that a derived class may call them. Because they are members of the superclass, they may touch its private data; because the derived class only calls them, it never touches that data itself. The path is drawn in Fig. 12.2-2.
Diagram
Fig. 12.2-2 – a large box “class Route” containing a small shaded box “private double ratePerKm” and, outside the shading but inside Route, a box “getRatePerKm()” with a short arrow reaching into the shaded box. Below, a separate box “class Fare extends Route” containing “computeFare()”, with an upward arrow labelled “calls” reaching getRatePerKm(). A dashed arrow drawn straight from computeFare() to ratePerKm, crossed out and labelled “not permitted”
The gain is not merely that the value can be fetched. Because every change now passes through a method of the superclass, the superclass can still impose conditions — a setter can reject a negative rate, round a value, or record that a change was made. A protected variable offers no such opportunity, since assignment to it is just assignment.
The program below puts the whole section together. routeName and distance are protected and are used in the derived class by name; ratePerKm is private and is reached only through its accessor.
12.3 Constructors in an Inheritance Chain
An object of a derived class holds two sets of variables: those described by its superclass and those added by itself. Section 12.2 dealt with who may use them. This section deals with an earlier question — who gives them their first values.
Recall from Class XI that a constructor is a member method with the same name as its class and no return type, not even void, which is executed automatically when an object of that class is created and whose usual work is to initialise the data members. A constructor that takes no arguments is a no-argument constructor; one that takes arguments is a parameterised constructor, and a class may have several, distinguished by their argument lists.
The awkwardness is this. The superclass’s variables belong to the superclass and are often private to it; only the superclass’s own constructor is in a position to set them. Yet it is a derived class object that is being created, so it is the derived class’s constructor that runs. Something must connect the two, and that something is the keyword super.
12.3.1 Why a Constructor Is Not Inherited
Every other kind of member travels down the hierarchy. A constructor does not. Two reasons, one mechanical and one about meaning, make this unavoidable.
The mechanical reason lies in the name. A constructor must carry the name of its own class. The constructor written inside Vehicle is named Vehicle, and inside a class Bus a method named Vehicle is not a constructor at all — it is just a method with a return type missing, which the compiler will reject. There is simply no way for Vehicle(...) to arrive in Bus and still be a constructor.
The reason about meaning is stronger. A constructor’s job is to leave an object of its class fully and correctly initialised. Vehicle’s constructor knows nothing of seats or route, because those variables did not exist when it was written. If it were inherited and used to build a SchoolBus, it would hand back a half-built object and report success. A class must take responsibility for initialising the members it declares, and only it can.
One related rule from Class XI is worth restating precisely, because it is often stated loosely. If a class declares no constructor of its own, the compiler supplies one that takes no arguments and does nothing beyond starting the superclass part; that supplied one is called the default constructor. The moment a class declares any constructor at all — even a parameterised one — the compiler supplies nothing. A class with only a parameterised constructor therefore has no no-argument constructor, and that single fact causes most of the errors in the next subsection.
12.3.2 Invoking the Superclass Constructor with super
Inside a constructor of the derived class, the statement
super(actual arguments);
calls the constructor of the immediate superclass whose parameter list matches those arguments. Written with brackets in this way, super is a call to a constructor, not a reference to a member; the other use of the keyword, super.method(), belongs to Section 12.4 and is a different thing entirely.
Four rules govern it, and all four are enforced by the compiler.
- If
super(...)is written, it must be the very first statement of the constructor. Nothing, not even an assignment, may precede it. - Its arguments must match some constructor of the immediate superclass in number and type.
- If you do not write it, the compiler silently inserts
super();— a call to the superclass’s no-argument constructor — as the first statement. - Consequently, if the superclass has no no-argument constructor and you omit
super(...), the program does not compile.
Rule 3 explains why constructors seemed to need no attention until now: in every hierarchy where each class has a no-argument constructor, the chaining happens without a word being written. Rule 4 explains the error that appears the moment a superclass is given a parameterised constructor.
12.3.3 The Order in Which Constructors Run
Because super(...) stands first, a chain of constructors is entered from the bottom and executed from the top. Creating a SchoolBus enters SchoolBus’s constructor, which immediately hands control to Bus’s, which immediately hands control to Vehicle’s. Vehicle’s body is the first body that actually runs to completion; control then unwinds, and each constructor finishes the work it had suspended. The path is drawn in Fig. 12.3-1.
Diagram
Fig. 12.3-1 – flow: new SchoolBus(…) → SchoolBus constructor entered, first statement super(r,s) → Bus constructor entered, first statement super(r) → Vehicle constructor body runs to completion → control returns, Bus constructor body runs to completion → control returns, SchoolBus constructor body runs to completion → object ready. Draw the three downward steps as one column of arrows labelled “entering” and the three upward steps as a second column labelled “executing”
This order is not an accident of implementation; it is the only order that can work. The body of a derived class’s constructor is allowed to use inherited members, so those members must already hold sensible values by the time it starts. Building from the top of the hierarchy downwards guarantees exactly that.
The program below makes the order visible by printing a line from each constructor.
| Step | What is executing | regNo |
seats |
route |
Printed |
|---|---|---|---|---|---|
| 1 | SchoolBus(...) entered; first statement is super(r, s), so control leaves at once |
null | 0 | null | — |
| 2 | Bus(...) entered; first statement is super(r), so control leaves at once |
null | 0 | null | — |
| 3 | Vehicle(...) body runs completely |
“BUS-07” | 0 | null | Vehicle part ready |
| 4 | control returns to Bus(...); its remaining statements run |
“BUS-07” | 42 | null | Bus part ready |
| 5 | control returns to SchoolBus(...); its remaining statements run |
“BUS-07” | 42 | “School Gate to Sector 5” | SchoolBus part ready |
| 6 | sb.show() runs on the finished object |
“BUS-07” | 42 | “School Gate to Sector 5” | Registration: BUS-07 / Seats: 42 / Route: School Gate to Sector 5 |
Result: the output begins with Vehicle part ready, although Vehicle’s constructor was the last to be asked for and the first to finish. The trace shows why — every constructor in the chain is entered before any constructor body completes, and the object is built from the top of the hierarchy downwards.
12.4 Redefinition of Variables and Methods in Subclasses
A derived class is not obliged to accept everything its superclass says. Along with adding new members, it may declare a member with a name already used above it — and this is called redefinition.
Java treats the two cases very differently, and the difference is the single most important idea in this section. When a variable is redefined, the subclass’s declaration merely covers the superclass’s, and both exist. When a method is redefined, the subclass’s version genuinely takes over. One of these is a trap and the other is the mechanism on which the rest of the chapter is built.
12.4.1 Redefining an Instance Variable
Suppose a superclass Period records that a school period runs for forty minutes, and a class LabPeriod derived from it wants that figure to be eighty. The obvious move is to declare minutes again inside LabPeriod.
What then happens is that the object holds two variables named minutes, one described by each class, as shown in Fig. 12.4-1. This is called variable hiding: inside the code of LabPeriod the name minutes refers to LabPeriod’s own variable and the inherited one is hidden, while super.minutes reaches past the hiding to the superclass’s variable.
Diagram
Fig. 12.4-1 – one rounded box labelled “one LabPeriod object” divided into two compartments, the upper labelled “declared in Period: minutes = 40” and the lower labelled “declared in LabPeriod: minutes = 80”. To the right, two labelled arrows: one from a box “code written inside LabPeriod” pointing to the lower compartment for the plain name minutes and, as a second dashed arrow, to the upper compartment for super.minutes; another from a box “code written inside Period” pointing only to the upper compartment
The last arrow in that figure is the one that catches people out. Code written inside Period was compiled knowing only Period’s variables, so it goes on using the forty even when it is running on a LabPeriod object. The program below shows exactly this happening.
That is why variable hiding is almost always a mistake rather than a technique. If a subclass needs a different value, the honest ways are to assign the inherited variable a new value in the subclass’s constructor, or to expose the value through a method that the subclass can override. Both leave one variable in the object, holding one truth.
12.4.2 Method Overriding
Methods behave in the opposite way, and the difference is deliberate. Declaring a method again in a derived class is called method overriding, and for objects of that class it genuinely takes the inherited version’s place.
The phrase “same name and same parameter list” is worth a name of its own. The signature of a method is its name together with the number, types and order of its parameters. Overriding requires the two signatures to match exactly; return types must match too, though from Java 5.0 onwards the overriding method may return a subclass of the type the superclass method returned.
Suppose a class Bill prints the details of a purchase at a school shop, and a class DiscountBill derived from it must print the same details and then a discount calculation. Declaring print() again in DiscountBill overrides it: for any DiscountBill object, print() now means the new version, and the superclass’s version is not carried out at all.
class DiscountBill extends Bill
{
void print()
{
System.out.println("Item: " + item);
System.out.println("Unit price: " + price);
System.out.println("Quantity: " + qty);
System.out.println("Amount payable: " + price * qty);
}
}Overriding is a genuine replacement, and that is exactly what makes this version unsatisfying: the first three lines have been copied out of the superclass, which is the duplication Section 12.1 set out to abolish. The repair is the subject of the next subsection.
Two rules limit what may be overridden. A private method cannot be overridden, because it is not visible in the subclass; a method with the same name declared there is simply a new and unrelated method. And an overriding method may not be less accessible than the one it overrides — a public method cannot be overridden by a protected one — because a subclass object must be usable everywhere a superclass object is usable.
12.4.3 Invoking the Overridden Method with super
An overriding method usually does not want to abolish the superclass’s version. It wants to do that work and then a little more. For this, super appears in its second form:
super.methodName(actual arguments);
Used this way, super is not a constructor call and need not stand first; it is an ordinary call, and it means “run the version of this method that my superclass declared, on this same object”. Without it, writing print() inside DiscountBill.print() would call DiscountBill.print() again — the method calling itself endlessly.
Diagram
Fig. 12.4-2 – two boxes, an upper labelled “class Bill” containing “void print()” and a lower labelled “class DiscountBill extends Bill” containing “void print()”. A thick arrow from the lower box to the upper labelled “super.print() — run the inherited version”; beside it a plain label on the lower box reading “print() on a DiscountBill object means this one”
12.4.4 Overloading Compared with Overriding
You met method overloading in Class XI: two or more methods in a class carrying the same name but different parameter lists, so that the compiler tells them apart by the arguments written at the call. Overriding also involves two methods of the same name, and the two ideas are confused so often that they are best set side by side.
| Feature | Method overloading | Method overriding |
|---|---|---|
| Where the two methods are | usually in one class; a subclass method may also overload an inherited one | one in the superclass, the other in the derived class |
| Parameter lists | must differ in number, type or order | must be identical |
| Return type | may be the same or different | the same, or a subclass of it |
| Inheritance | not needed | essential |
| Which method runs is decided | at compile time, from the arguments written at the call | at run time, from the class of the object the call is made on |
| Purpose | to offer one operation in several convenient forms | to give a specialised class its own version of an inherited operation |
| Commonly called | compile-time polymorphism | run-time polymorphism |
The fifth row is the real distinction, and everything else follows from it. In overloading, the compiler can see the arguments and settle the matter there and then. In overriding, the arguments are identical in both versions, so the arguments cannot decide anything; the only thing that differs is which kind of object the method is being carried out on, and that is not always known until the program runs.
The last row of the table uses a word this chapter has not yet defined. Polymorphism is one name standing for more than one behaviour; overloading and overriding are its two forms, and the second — the form that only inheritance makes possible — is the doorway to Section 12.6.
12.5 The Class Object
Every class written so far in this chapter has fallen into one of two groups: those that name a superclass with extends, and those that do not. That division turns out to be less real than it looks. In Java there is no class of the second kind — only classes whose superclass is not written down.
12.5.1 The Root of Every Class Hierarchy
Java supplies a class named Object, which belongs to the package java.lang and is therefore available in every program without an import statement. It occupies a position no other class does.
So Vehicle, in Section 12.3, extends Object although its declaration says nothing of the kind, and SchoolBus is descended from Object at three removes. Draw all the classes of a Java program together and you do not get a scatter of separate hierarchies; you get one tree, with Object at its root, as in Fig. 12.5-1.
Diagram
Fig. 12.5-1 – a box labelled “Object” at the top, drawn wider than the rest. Below it, joined by dashed upward arrows labelled “extends Object, written by no one”, three boxes: Vehicle, Bill and Distance. Below Vehicle a solid arrow up from Bus, and below Bus a solid arrow up from SchoolBus; below Bill a solid arrow up from DiscountBill. The solid arrows are labelled “extends, written by the programmer”
Two consequences follow, and both matter.
The first is that every object in Java, of every class, already possesses a small set of methods before its own class declares anything. They are inherited from Object in the ordinary way described in Section 12.1, and a few of them are used constantly.
| Method | What it gives back | Behaviour if not overridden |
|---|---|---|
toString() |
a String describing the object |
the name of the object’s class, an @, and a code in hexadecimal |
equals(Object o) |
boolean |
true only when o refers to the very same object |
hashCode() |
int |
a code derived from the object, different objects normally giving different codes |
getClass() |
an object describing the class | reports the class the object actually belongs to, whatever the reference’s type |
The default toString() is worth seeing once. Printing an object of a class that has not overridden it produces something of the form Distance@1b6d3586, in which the part after the @ differs from one run to the next. It tells you the class and nothing else, which is rarely what you wanted — and that is the whole motivation for the next subsection.
The second consequence is about types. Because every class is descended from Object, a variable declared of type Object may refer to an object of any class whatever. That single fact is what lets Java’s library be written once and used with classes that did not exist when it was written. It is also the first hint of the idea developed in Section 12.6.
12.5.2 Redefining toString() and equals()
The methods in Table 12.9 are inherited, so they may be overridden exactly as any other inherited method is. Nothing new is needed: the rules of Section 12.4.2 apply unchanged, including the rule that an overriding method may not be less accessible than the one it overrides. Since Object declares these methods public, your versions must be declared public too.
Overriding toString() is the more useful of the two and the easier to justify. Java calls it automatically wherever an object is used in a place that requires a String — when an object is joined to a string with +, and when an object is passed to System.out.println(). Give a class a sensible toString() and its objects become printable in one line, everywhere, for ever.
Overriding equals() answers a different question: when should two distinct objects count as the same? The inherited version says “never, unless they are literally one object”. For a class such as Distance, where two separately created objects may well record the same length, that answer is unhelpful, and the class must supply its own.
The two comparison lines of the output are the point of the program, and they deserve a table of their own.
| Feature | == |
equals() |
|---|---|---|
| What it is | an operator built into the language | a method inherited from Object |
| What it compares, for objects | the two references — whether they lead to one and the same object | whatever the class decides, if the class overrides it |
| Can the programmer change its meaning | no | yes, by overriding it |
Behaviour if equals() is not overridden |
compares references | compares references, giving the same result as == |
Result for a and b above |
false, because two objects were created |
true, because both record 12500 metres |
For primitive types such as int and char, == compares values in the ordinary way and no confusion arises. The trouble begins the moment == is used between two object references — including two String references — where it asks a question about identity, not about content.
One convention should be mentioned honestly. Java’s own library classes assume that two objects reported equal by equals() return the same value from hashCode(). A class that overrides equals() and leaves hashCode() alone will behave correctly in a program such as the one above, but may behave oddly if its objects are later stored in a library structure that groups items by hash code. The safe habit is to override the two together.
12.6 Subclass Polymorphism and Dynamic Binding
Section 12.4 defined polymorphism as one name standing for more than one behaviour, and named its two forms. The compile-time form was already familiar from Class XI. This section takes the other one — the form that only inheritance can produce — and it is the point towards which the whole chapter has been building.
Two facts make it work, and neither is obvious. The first is that a reference variable declared of a superclass type is allowed to refer to an object of any class derived from it. The second is that when an overridden method is called through such a reference, the version that runs belongs to the object, not to the reference. Take these in turn.
12.6.1 One Superclass Reference, Many Subclass Objects
A reference variable has a declared type; an object has a class. Until now the two have always matched. They need not.
Parcel p = new SpeedParcel("P102", 2.0);This is legal Java, provided SpeedParcel is derived from Parcel. The compiler permits it for the reason given in Section 12.1.5: a SpeedParcel is a Parcel, so a variable that promises to refer to a parcel has not been made to break its promise. The conversion from the subclass type to the superclass type happens automatically, with nothing written, and the ability this gives has a name: subclass polymorphism.
The reverse assignment is not allowed. SpeedParcel s = new Parcel(...); is rejected, and rightly: an ordinary parcel is not a speed parcel and would be missing whatever a speed parcel adds.
There is a price, and it is easy to forget. The compiler checks every call against the declared type of the reference, not against the object it happens to hold. Through a Parcel reference you may use only the members Parcel declares — even when the object at the other end is a SpeedParcel with members of its own. Fig. 12.6-1 draws this as a window: the object is whole, but the reference lets you see only part of it.
Diagram
Fig. 12.6-1 – on the left a small box labelled “Parcel p” with an arrow running right to a large rounded box labelled “a SpeedParcel object”. The large box has two compartments, the upper labelled “Parcel part: docket, weight, charge(), printCharge()” and the lower labelled “SpeedParcel part: tracking()”. A narrow rectangular frame labelled “what the reference p allows” is drawn over the upper compartment only, with the lower compartment outside it and marked “present in the object, but not reachable through p”
12.6.2 Dynamic Binding: Choosing the Method at Run Time
Now the second fact, and it points the opposite way from the first. The compiler decides whether a call is allowed from the reference’s declared type. Java decides which version to carry out from the object’s actual class — and it does so while the program is running. That second mechanism is called dynamic binding.
Its opposite is static binding, also called early binding, in which the compiler settles the matter and nothing can change it afterwards. Static binding governs instance variables, and it is exactly why the redefined variable in Section 12.4.1 behaved as it did: the name minutes in a method of Period was bound to Period’s variable when Period was compiled, whatever object the method later ran on. Methods behave the other way, and the following program shows it.
i |
Class of the object in list[i] |
Version of charge() carried out |
Value it returns | Line printed |
|---|---|---|---|---|
| 0 | Parcel |
Parcel.charge() |
2.0 × 40.0 = 80.0 | P101 : 80.0 |
| 1 | SpeedParcel |
SpeedParcel.charge(), which calls Parcel.charge() through super |
80.0 × 2 = 160.0 | P102 : 160.0 |
| 2 | LocalParcel |
LocalParcel.charge(), which calls Parcel.charge() through super |
80.0 ÷ 2 = 40.0 | P103 : 40.0 |
Result: the three lines of the output above. The trace shows that the column which changes is the second one — the class of the object — and that everything else about the call is identical each time round the loop.
| Feature | Static binding | Dynamic binding |
|---|---|---|
| When the choice is made | at compile time | while the program is running |
| What settles the choice | the declared type of the reference | the class of the object referred to |
| Applies to | instance variables and overloaded methods | overridden methods |
| Also called | early binding | late binding, run-time binding |
| Effect of using a superclass reference | the superclass’s variable is used | the subclass’s method is used |
| Can it be predicted by reading one class alone | yes | not always; it depends on the object supplied |
The fifth row is the one worth memorising, because it is the sentence that resolves almost every question of this kind: through a superclass reference, variables come from the reference and methods come from the object.
12.6.3 What Polymorphism Buys the Programmer
It is fair to ask what all this is for, since the parcel program could have been written with three separate arrays and no inheritance at all.
The answer is what happens next. Suppose the courier now offers a fourth kind of parcel, charged at one and a half times the ordinary rate. You write one new class extending Parcel and overriding charge(). Nothing else changes: not printCharge(), not the array, not the loop, not one line of main() beyond the object being created. Code that was written and tested before the new class existed goes on working with it. A program built this way is open to new kinds without being reopened for repair, and that — far more than the saving of a few duplicated lines — is why inheritance is worth the trouble.
The second gain is uniform treatment. A group of related objects can be held in one array, passed to one method and processed by one loop, precisely because the superclass guarantees that every one of them answers to charge(). The superclass has become a promise about behaviour rather than merely a store of common data.
Three honest limitations belong beside those gains.
First, the superclass reference narrows what you can reach. Anything a subclass adds is invisible through it, and getting at it needs an instanceof test and a cast — which, if it starts appearing all over a program, is usually a sign that the operation should have been declared in the superclass and overridden instead.
Second, reading the program becomes harder, not easier. list[i].printCharge() tells you nothing about what will be printed. To know, you must find which class the object belongs to, and that may have been decided far away.
Third, the superclass must be designed for it. charge() had to exist in Parcel for any of this to work. If a superclass is written without thought for what its subclasses will need to redefine, its subclasses cannot use polymorphism however hard they try — and that is the difficulty which leads directly to the abstract classes of Section 12.7.
12.7 Abstract Classes
Section 12.6 ended on a difficulty. Polymorphism works only if the superclass declares the operation that the subclasses redefine — and sometimes the superclass has nothing sensible to put in it.
Parcel was fortunate: an ordinary parcel really does have a charge, and the superclass could compute it. Now think of a class Shape, with Rectangle and Triangle derived from it. Every shape has an area, so area() certainly belongs in Shape. But what would its body contain? There is no formula for the area of a shape in general; there are only formulas for particular shapes. And what would an object of Shape itself represent — a shape that is no shape in particular?
Java’s answer to both questions is the abstract class.
12.7.1 A Class That Should Never Be Instantiated
A class declared with the keyword abstract — an abstract class — may be inherited, but no object of it may be created.
abstract class Shape
{
protected String name;
Shape(String n)
{
name = n;
}
}Writing new Shape("something") anywhere in the program is now a compile-time error, and that is the whole of the restriction. Everything else about the class is unchanged. An abstract class may have instance variables, ordinary methods with bodies, and — as above — constructors. Its constructor is not wasted: a subclass calls it with super(...), exactly as in Section 12.3, and it initialises the inherited part of the subclass’s object.
A class that is not abstract is called a concrete class. Rectangle and Triangle will be concrete; objects of them can be created in the usual way.
One consequence is easy to state and easy to misread. You may not create a Shape object, but you may declare a Shape reference:
Shape s;That is not a contradiction. A reference variable holds no object; it merely says what kind of object it is allowed to refer to. Assigning s = new Rectangle(8.0, 5.0); is perfectly legal, since a rectangle is a shape. So an abstract class can still serve as the common type of Section 12.6.1 — indeed that is one of the two things it is for. The other is the subject of the next subsection.
12.7.2 Abstract Methods
The second thing an abstract class can do is declare a method without writing it. Such a declaration is an abstract method.
abstract double area();Note the semicolon where a body would be. Three rules follow from this declaration, and the compiler enforces all three.
- A class containing even one abstract method must itself be declared
abstract. A class cannot promise a method it does not have and still allow objects to be made. - Every concrete subclass must override every abstract method it inherits. Failing to do so is a compile-time error, not a silent gap.
- A subclass that overrides only some of them must itself be declared
abstract, and passes the remaining obligation down to its subclasses.
Rule 2 is the reason abstract methods exist. In Section 12.6 the superclass could only hope that its subclasses would override charge(); if one forgot, the inherited version ran and the mistake went unnoticed. An abstract method converts that hope into a requirement checked by the compiler before the program will even run. The whole arrangement is drawn in Fig. 12.7-1.
Diagram
Fig. 12.7-1 – an upper box labelled “abstract class Shape”, drawn with a dashed outline, listing the variable name, the constructor Shape(String), the entry “abstract double area();” marked “declared, no body” and the method show() marked “written in full”. Beside it, the text “new Shape()” crossed out and labelled “no object of this class”. Below, two solid boxes “class Rectangle” and “class Triangle”, each listing its own dimensions and its own double area() marked “body supplied here”, joined to the Shape box by upward arrows labelled “extends”. A dashed arrow from show() curving down to both area() bodies, labelled “chosen at run time”
| Feature | Abstract method | Method with an empty body |
|---|---|---|
| How it is written | abstract keyword, no braces, ends with a semicolon |
no abstract keyword, a pair of empty braces |
| Return type allowed | any, since the subclass supplies the return | only void, since nothing is returned |
| Effect on its class | the class must be declared abstract |
none; the class may stay concrete |
| Is a subclass obliged to override it | yes, if the subclass is concrete | no |
| What happens when it is called | the subclass’s version runs; this class has none | nothing at all happens |
| What it communicates | every subclass must decide this for itself | this operation exists but has nothing to do here |
Now the two ideas can be put together in one program.
12.7.3 A Class as an Abstract Data Type
Step back from the keyword and look at what Shape actually is. It says that a shape has a name and an area. It does not say how an area is obtained, and it holds no length, no breadth and no base. Anyone writing code against Shape can use the operation without knowing the representation — and different subclasses use entirely different representations to satisfy the same promise.
That arrangement has a name.
The two Java features you have met make an abstract data type (ADT) possible. Encapsulation, from Section 12.2, keeps the representation private so that no user of the type can depend on it. Abstract methods fix the operations so that every implementation must provide them. What remains visible is a set of promises about behaviour, which is exactly what an ADT is.
This is worth recognising because it is how the data structures of your later work are described: a stack is not a particular arrangement of memory but a type on which you may push, pop and test for emptiness; a queue is a type you may add to at one end and remove from at the other. Two different classes may implement a stack in two different ways, and any program written against the operations will work with both.
An honest qualification closes the section. An abstract class makes a partial abstract data type, because along with the promises it also carries data members and written-out methods that every subclass is forced to inherit. Shape obliges each of its subclasses to have a name, whether that suits them or not. For an abstract data type in its pure form — operations and nothing else, imposed on classes that need share no data and no ancestry at all — Java offers a separate construct, and it is the subject of Section 12.8.
12.8 Interfaces in Java
Section 12.7 closed on a promise: that Java has a construct for stating what a class must be able to do, imposed on classes that share no data and no ancestry. That construct is the interface, and it settles the question left open all the way back in Section 12.1.4.
12.8.1 Why Java Needs Interfaces
Two difficulties have accumulated, and one construct answers both.
The first is multiple inheritance. Java forbids a class to extend two classes, because if both wrote a body for the same method there would be no honest way to decide which body an object should use. Note carefully where the ambiguity lies: in the bodies. If neither class supplied a body — if both merely stated that the method must exist — there would be nothing to clash. A class could take such a statement from any number of sources without contradiction.
The second is that classes belong to families, but behaviour does not. A Book is a library item and takes its place in that hierarchy. Yet the school also lends out a projector, which is no kind of library item at all, and both need to be charged rent. “Can be rented” is not a place in the hierarchy; it is a role, and it cuts across the tree, as Fig. 12.8-1 shows. An abstract class cannot express it, because a class may extend only one superclass and Projector has no business extending LibraryItem.
Diagram
Fig. 12.8-1 – on the left a small hierarchy: a box “LibraryItem” with two boxes “Book” and “Magazine” beneath it joined by upward arrows labelled “extends”. On the right, an unconnected box “Projector”. Across the whole figure a horizontal dashed band labelled “Rentable” passes through the Book box and the Projector box only, leaving LibraryItem and Magazine outside it. Caption line: “a role that is not a place in any hierarchy”
An interface answers both. It states operations and supplies no bodies, so nothing can clash and a class may take on as many interfaces as it needs. And it is independent of the inheritance tree, so entirely unrelated classes may share a role.
12.8.2 Defining an Interface
An interface is written much like a class, with the keyword interface in place of class.
interface interface_name
{
constants
method declarations without bodies
}
Here is one for the role of Fig. 12.8-1.
interface Rentable
{
int MAX_DAYS = 14;
double rentFor(int days);
}Two things in those four lines are not what they appear to be, because the compiler supplies modifiers that you are not obliged to write.
| As written in the interface | What it actually means | Consequence |
|---|---|---|
int MAX_DAYS = 14; |
public static final int MAX_DAYS = 14; |
it is a constant shared by everyone, and it must be given a value where it is declared |
double rentFor(int days); |
public abstract double rentFor(int days); |
it has no body, and any class implementing it must supply one |
Three rules follow, and they are worth stating plainly. An interface cannot have instance variables — a variable declared in it is a constant, the same for every object of every class that implements it, and it can never be assigned to afterwards. An interface cannot have constructors, since no object of it is ever created. And in the form used throughout this book, an interface declares no method bodies at all; later versions of Java relax this, but nothing here needs the relaxation.
An interface may, however, be extended by another interface, using extends exactly as a class does:
interface Trackable
{
String assetCode();
}
interface Traceable extends Trackable
{
String lastSeenAt();
}A class implementing Traceable must now supply both methods. Note the keyword: one interface extends another; it does not implement it, because there is nothing to implement.
12.8.3 Implementing an Interface through a Class
A class announces that it provides an interface’s methods with the keyword implements:
class classname implements interface_name
{
class body
}
The class must then define every method the interface declares, or else be declared abstract itself and pass the obligation to its own subclasses. This is exactly the rule for abstract methods in Section 12.7.2, and for the same reason.
One extra rule catches almost everybody once. Every method of an interface is public, and Section 12.4.2 forbids an overriding method to be less accessible than the method it overrides. The implementing methods must therefore be declared public, even though the interface did not say so.
12.8.4 Implementing More Than One Interface
A class may implement any number of interfaces, named after implements and separated by commas. It may do this while also extending a class — in which case extends is written first:
class classname extends superclass implements Interface1, Interface2
The class must then supply a body for every method of every interface it names. There is no ambiguity to resolve, because no interface brought a body with it. This arrangement is drawn in Fig. 12.8-2.
Diagram
Fig. 12.8-2 – a box “class Cycle” at the bottom centre. A solid upward arrow from it, labelled “extends”, to a box “class Equipment” above it. Two dashed upward arrows from the same box, labelled “implements”, going to two boxes drawn with dashed outlines on either side, “interface Rentable” and “interface Trackable”. A note beside the solid arrow reads “at most one”; a note beside the dashed arrows reads “as many as required”
12.8.5 Interfaces for User Defined Implementation of Behaviour
So far an interface has described what a class is able to do. It has a second and more powerful use: letting the writer of one class leave a decision to whoever uses it.
Consider a class that sorts an array. The sorting itself — comparing two items and exchanging them — is the same everywhere and should be written once. But whether the result should run from low to high or high to low, and which field of a record decides the order, only the user of the class knows, and the user may not have been born when the sorting class was written.
The class cannot ask, and it should not guess. What it can do is declare an interface containing the one operation it lacks, take a reference of that interface type as an argument, and call it. Whoever uses the class writes a small class implementing the interface and hands over an object of it. The behaviour is supplied by the user; the sorting stays where it belongs.
Outer pass i |
Comparisons made by inOrder(), and the action taken |
Array after the pass |
|---|---|---|
| 0 | (56, 91) in order, left alone; (91, 78) not in order, exchanged; (91, 63) not in order, exchanged | 56, 78, 63, 91 |
| 1 | (56, 78) in order, left alone; (78, 63) not in order, exchanged | 56, 63, 78, 91 |
| 2 | (56, 63) in order, left alone | 56, 63, 78, 91 |
Result: the array leaves the method as 56, 63, 78, 91, which is the first line of the output. The trace shows that the only decision the sort ever makes is the one it gets back from inOrder() — replace that with the version in Descending and every exchange in the table reverses.
12.8.6 Abstract Class Compared with Interface
Both an abstract class and an interface state operations that other classes must supply, and neither can be used to create an object. They are not, however, alternatives to be chosen by taste.
| Feature | Abstract class | Interface |
|---|---|---|
| Declared with | abstract class |
interface |
| A class connects to it using | extends |
implements |
| How many may one class take | at most one | any number |
| Methods it may contain | both abstract methods and methods with bodies | method declarations without bodies |
| Variables it may contain | ordinary instance variables, of any access | constants only, implicitly public static final |
| Constructors | may have them, called by subclasses through super |
cannot have them |
| Objects of it | cannot be created | cannot be created |
| What it expresses | what a thing is — a place in a family | what a thing can do — a role |
The last row is the one that decides between them in practice. Ask whether the classes involved are genuinely of one kind — the is-a test of Section 12.1.5. A rectangle and a triangle are both shapes, and share a name and a way of displaying themselves, so Shape is properly an abstract class. A cycle and a projector are not two kinds of one thing; they merely both happen to be rentable, so Rentable is properly an interface.
Nothing forbids using both at once, and the program of Section 12.8.4 did exactly that: Cycle took its identity from Equipment and its two roles from two interfaces.
12.8.7 The Java interface and the Everyday Word “Interface”
One caution closes the chapter, because the word carries two meanings in computer science and they are easily run together.
In ordinary usage, the interface of a class means the set of members it offers to the outside world — the public methods a user of the class may call, as against the private workings behind them. Every class has an interface in this sense, whether or not the keyword appears anywhere in the program. When a data structure is described as an abstract data type with a well-defined interface, it is this meaning that is intended.
In Java, interface is a keyword naming a particular construct: a declaration of constants and bodiless methods, written with interface, connected to a class with implements. It is one way of writing down an interface in the first sense, and a very disciplined one, since it forces every implementing class to provide exactly what was promised.
The relationship is worth stating exactly. Every Java interface describes an interface in the general sense. Not every interface in the general sense is a Java interface — the public methods of an ordinary class are its interface too. When you meet the word, ask which is meant; the keyword, in interface_name form and in a listing, always means the construct.
12.9 Summary
Inheritance and its forms
- Inheritance is the mechanism by which one class acquires the data members and member methods of another, uses them as though it had declared them, and adds members of its own. The keyword is
extends. - The class whose description is acquired is the superclass, also called the base or parent class; the class acquiring it is the derived class, also called the subclass or child class. Only the derived class names the other.
- An object of a derived class holds the inherited members and the added ones together; it is one object, not two.
- A derived class inherits instance variables and member methods. It does not inherit constructors, it cannot directly reach private members, and it cannot alter the superclass.
- The gain is reusability: code written and tested once serves many classes, a correction in the superclass reaches every class below it, and inheritance is transitive down a chain. The cost is tight coupling — a change in the superclass ripples into classes its author never saw.
- Java supports single inheritance (one derived class, one superclass), hierarchical inheritance (several derived classes, one superclass) and multilevel or nested inheritance (a derived class itself extended). Hybrid inheritance is any combination of these.
- Multiple inheritance — one class extending two or more classes — is not allowed between classes in Java, because two superclasses could supply two bodies for the same method and there would be no honest way to choose. Interfaces serve where it is needed.
- Write
B extends Aonly when the is-a relationship holds — when an object ofBgenuinely is an object ofA. Where one class merely needs to use another, the has-a relationship is right: hold an object of it as a variable. Reuse is a benefit of inheritance, not a reason for it.
Member access in a derived class
- Every member has exactly one access specifier:
private, default (none written),protectedorpublic. Each level reaches one place further than the one above it. - A
privatemember is usable only inside its own class. It is present in every subclass object but is not accessible there by name. - A member with no specifier is not public: it is usable throughout its own package only, and so is invisible to a derived class in another package.
- A
protectedmember is usable in its own class, anywhere in its package, and in any derived class even outside the package. - A
publicmember is usable everywhere, by derived and unrelated classes alike. - Java has no public or private mode of inheritance. Every inherited member keeps exactly the access it was given in the superclass.
- Where a derived class needs a private value of its superclass, the superclass supplies an accessor method (getter) to return it and, where it must be changed, a mutator method (setter) to receive it. Because the change then passes through a method of the superclass, conditions can still be enforced — which a
protectedvariable cannot do.
Constructors in an inheritance chain
- A constructor is not inherited: it carries the name of its own class, and it exists to complete an object of that class, of whose added members it knows nothing.
- If a class declares no constructor, the compiler supplies a default constructor taking no arguments. The moment any constructor is declared, that supplied one disappears.
super(...)invokes a constructor of the immediate superclass. It may appear only in a constructor and must be its first statement.- If
super(...)is not written, the compiler inserts a call to the superclass’s no-argument constructor. A superclass having only a parameterised constructor therefore causes a compile-time error unlesssuper(...)is written explicitly. - Constructors are entered from the bottom of the chain and completed from the top, so the most general part of an object is always finished first. Instance variables receive their default values — 0,
false,null— before any constructor body runs.
Redefinition in subclasses
- Redefining an instance variable hides it: both variables exist in the object, the derived class’s own name refers to its own, and
super.namereaches the inherited one. Code written in the superclass goes on using the superclass’s variable. Hiding is legal and almost always a mistake. - Method overriding is the declaration in a derived class of a method with the same signature — name and parameter list — and the same return type as an inherited method, so that the derived class’s version is used for its objects.
- An overriding method may not be less accessible than the one it overrides, and a
privatemethod cannot be overridden at all. - Change the parameter list and nothing is overridden; a second, unrelated method is declared, and the inherited one goes on being used. This error is silent.
super.methodName(...)calls the superclass’s version of an overridden method. It is an ordinary statement and may stand anywhere in a method. Withoutsuper, the call names the method that is already running.- Polymorphism is one name standing for more than one behaviour. Overloading — same name, different parameter lists, one class — is settled by the compiler from the arguments written, and is called compile-time polymorphism. Overriding is settled while the program runs and is called run-time polymorphism.
The class Object
- Every class in Java is descended from
Object, which belongs tojava.lang. A class declaring no superclass hasObjectas its immediate superclass, so all classes form one tree. - Every object therefore inherits
toString(),equals(),hashCode()andgetClass()before its own class declares anything. Left unoverridden,toString()reports only the class name and a code, andequals()is true only for one and the same object. toString()is called automatically when an object is joined to a string or passed toprintln(), so overriding it makes a class printable everywhere.- An overriding
equals()must take an argument of typeObject; declaring it of the class’s own type changes the signature and overrides nothing. A class that overridesequals()should overridehashCode()with it. - For objects,
==compares references and asks whether they lead to one and the same object;equals()compares whatever the class decides, and compares references only when the class has not overridden it.
Subclass polymorphism and dynamic binding
- Subclass polymorphism is the ability of a superclass reference to refer to an object of any class derived from it, so that one variable, array element or argument handles several related classes. The reverse assignment is not allowed.
- The compiler checks every call against the declared type of the reference, so members added by a subclass are unreachable through a superclass reference. Reaching them needs an
instanceoftest followed by a cast. - Dynamic binding is the choosing of the version of an overridden method while the program runs, from the class of the object rather than the type of the reference. It is also called late or run-time binding.
- Static or early binding is its opposite and governs instance variables and overloaded methods. In one sentence: through a superclass reference, variables come from the reference and methods come from the object.
- Polymorphism lets a new subclass be added without altering the code that uses the family, and lets related objects be held and processed uniformly. It also narrows what a reference can reach, makes a program harder to read at the point of call, and works only if the superclass was designed with the operation in it.
Abstract classes and abstract data types
- An abstract class is declared with
abstractand no object of it can be created. It may still hold instance variables, ordinary methods and constructors, and its constructor is called by its subclasses throughsuper. A class that is not abstract is a concrete class. - A reference of an abstract class type is perfectly legal and may refer to any of its subclass objects.
- An abstract method is declared with
abstract, has no body, and ends in a semicolon. A method with an empty pair of braces is not abstract; it is an ordinary method that does nothing. - A class with even one abstract method must be declared abstract; every concrete subclass must override every abstract method it inherits, and a subclass that overrides only some must itself be abstract.
- Abstract methods turn a superclass’s hope that its subclasses will override a method into a requirement the compiler enforces.
- An abstract data type (ADT) is a type defined by the operations that may be performed on it and by what they do, not by how its data is stored or its operations carried out. Encapsulation hides the representation and abstract methods fix the operations. An abstract class gives only a partial ADT, since it also imposes data and ancestry.
Interfaces
- An interface is a named collection of constants and of method declarations without bodies. No object of it can be created and it has no constructor.
- Because it supplies no bodies, nothing can clash, so a class may take on any number of interfaces; and because it stands outside the inheritance tree, unrelated classes may share the role it names.
- A method declared in an interface is implicitly
publicandabstract. A variable declared in it is implicitlypublic,staticandfinal— a constant that must be given its value where it is declared. An interface has no instance variables. - A class states that it provides an interface’s methods with
implements, and must define every one of them or be declared abstract itself. Those methods must be declaredpublic, since an overriding method may not be less accessible. - A class may extend at most one class and implement any number of interfaces, with
extendswritten beforeimplements. One interface may build on another, and the keyword for that isextends, notimplements. - An interface name is usable as a reference type, so dynamic binding extends across classes with no common ancestor. Such a reference can call only the methods the interface declares.
- An interface also lets the writer of a general class leave one decision to whoever uses it: the class works entirely through the interface, and the user supplies a class implementing it. This is what the syllabus means by an interface used for user defined implementation of behaviour.
- Choose between the two constructs by what they express. An abstract class says what a thing is and suits classes that form one family and share data; an interface says what a thing can do and suits classes that share only a capability, or that already have superclasses of their own.
- The word interface has two senses. In ordinary usage it means the set of public method prototypes a class offers, which every class has. In Java it names a construct declared with the keyword
interface. The construct is one disciplined way of writing down an interface in the first sense.
12.10 Key Terms
abstract— the Java keyword that marks a class as one whose objects cannot be created, or a method as one declared without a body.- abstract class — a class declared with
abstract, of which no object can be created, written to be extended by other classes. - abstract data type (ADT) — a type defined by the operations that may be performed on it and by what they do, and not by how its data is stored or its operations are carried out.
- abstract method — a member method declared with
abstract, having no body and ending in a semicolon, which every concrete subclass must override. - access specifier — the keyword written before a member that fixes where in the program that member may be used:
private, default,protectedorpublic. - accessor method — a method of a class that returns the value of one of that class’s own data members; also called a getter.
- ADT — abstract data type.
- cast — a class name written in brackets before a reference, telling the compiler to treat that reference as being of the named type.
- concrete class — a class that is not abstract, and of which objects can therefore be created.
- constructor — a member method carrying the same name as its class and having no return type, run automatically when an object is created, whose work is to initialise the data members.
- default constructor — the no-argument constructor the compiler supplies to a class that declares no constructor of its own.
- derived class — a class that acquires the members of another class by naming it with
extends; also called the subclass or child class. - dynamic binding — the choosing of the version of an overridden method while the program is running, from the class of the object rather than the type of the reference; also called late or run-time binding.
- encapsulation — keeping an object’s data inside the object and allowing the outside world to reach it only through the object’s own methods.
equals()— a method inherited fromObjectthat reports whether two objects are to be regarded as the same, comparing references unless the class overrides it.extends— the Java keyword by which a class names the one superclass whose members it acquires, and by which one interface builds on another.- has-a relationship — the relationship in which one class holds an object of another as a data member and uses it, without claiming to be one.
- hierarchical inheritance — the form in which several derived classes extend the same superclass.
- hybrid inheritance — a structure combining more than one form of inheritance.
implements— the Java keyword by which a class undertakes to supply the methods declared in one or more interfaces.- inheritance — the mechanism by which one class acquires the data members and member methods of another class and may add members of its own.
instanceof— an operator that reports whether the object referred to on its left belongs to the class or interface named on its right.- interface — in Java, a construct declared with the keyword
interface, holding constants and method declarations without bodies, which classes may implement; in ordinary usage, the set of public method prototypes a class offers to the outside world. - is-a relationship — the relationship that must hold for inheritance to be sound: every object of the derived class must genuinely be an object of the superclass.
- method overloading — declaring several methods with the same name but different parameter lists, the compiler deciding between them from the arguments written.
- method overriding — declaring in a derived class a method with the same signature and return type as an inherited one, so that it is used in place of the inherited version for objects of that class.
- multilevel inheritance — the form in which a derived class is itself extended, so that the chain runs through more than one level; also called nested inheritance.
- multiple inheritance — one class extending two or more classes at once, which Java does not allow between classes.
- mutator method — a method of a class that receives a value and assigns it to one of that class’s own data members; also called a setter.
Object— the class injava.langfrom which every other class in Java is descended.- package — a named group of classes kept together, in practice the folder in which they are compiled.
- parameterised constructor — a constructor that takes one or more arguments.
- polymorphism — one name standing for more than one behaviour; its two forms are compile-time polymorphism, produced by overloading, and run-time polymorphism, produced by overriding.
- protected visibility — the access given by
protected, allowing a member to be used in its own class, anywhere in its package, and in any derived class even outside the package. - reusability — the use of code written and tested once by many classes, without its being copied.
- signature — a method’s name together with the number, types and order of its parameters.
- single inheritance — the form in which one derived class extends one superclass.
- static binding — the settling of a call by the compiler, from the declared type of the reference; it governs instance variables and overloaded methods, and is also called early binding.
- subclass polymorphism — the ability of a superclass reference to refer to an object of any class derived from it.
super— the Java keyword used with brackets to call a superclass constructor, and with a dot to call a superclass method or reach a hidden superclass variable.- superclass — the class whose members are acquired by another through
extends; also called the base class or parent class. toString()— a method inherited fromObjectthat returns aStringdescribing the object, called automatically when an object is printed or joined to a string.- variable hiding — declaring in a derived class an instance variable with the same name as an inherited one, so that both exist in the object and the inherited one is reached with
super.
12.11 Points to Ponder
Code can be reused in two ways: a class may extend another, or it may simply hold an object of another as a data member and call its methods. The second way creates no dependence on the superclass, and nothing forces a change in one class to ripple into the other. What, then, does inheritance give in return for the dependence it creates — and can you describe a program in which that return is not worth the price?
Java forbids a class to extend two classes, on the ground that two superclasses might supply two bodies for the same method with no honest way to choose between them. Yet a class may implement two interfaces that declare the same method, and it then writes one body serving both promises — which may suit one interface’s intentions better than the other’s. Has the language solved the problem of multiple inheritance, or moved it from the compiler to the programmer?
Redefining an instance variable in a subclass is legal Java, produces two variables of the same name in one object, and is almost never what anybody wants. The designers of the language could have forbidden it, as they forbade so much else. Is there value in leaving a legal but unwise construct available, or should a language rule out what its designers know to be a mistake?
Polymorphism is praised because a new subclass can be added without altering a line of the code that uses the family. But it also means that reading
list[i].charge()tells you nothing whatever about what will be computed, since that depends on an object created somewhere else. Extensibility has been bought with readability — which of the two matters more, and does your answer change according to who will have to maintain the program next year?An abstract method turns a hope into a requirement: the superclass no longer trusts its subclasses to supply the operation, and the compiler refuses to proceed without it. Look through a program you have written and find something that is at present only a hope — a comment, a naming convention, an agreement between you and a classmate. Could it be turned into something the compiler checks, and what would be lost if it were?
Every class in Java descends from
Object, so every object you create carriestoString(),equals()andhashCode()whether the class has any use for them or not. A class may redefine whatequals()means for its own objects, but no class can redefine what==means. Would the language be better or worse if the meaning of==could be settled by each class in the same way?
12.12 Objective Questions
- Which one of the following is not inherited by a derived class from its superclass? [1 mark]
- a
publicmember method - a
protectedinstance variable - a constructor
- a method the superclass itself inherited from
Object
- In Java, one class may extend ______ class or classes and implement ______ interface or interfaces. [1 mark]
- one; one
- one; any number of
- any number of; one
- any number of; any number of
- An overriding method may not: [1 mark]
- carry the same name as the method it overrides
- be less accessible than the method it overrides
- call the method it overrides
- belong to a class that declares a constructor
- Which one of the following statements about a Java interface is correct? [1 mark]
- It may declare instance variables.
- It may declare a constructor.
- Its methods are
publicwhether the word is written or not. - A class connects itself to it with
extends.
- A
Shapereference refers to aCircleobject, and both classes declare a methodarea(). The version ofarea()that is carried out is settled by: [1 mark]
- the declared type of the reference, at compile time
- the class of the object, while the program is running
- the order in which the two classes were compiled
- the access specifier written on the method
- Assertion (A): A class whose only constructor is parameterised cannot be extended unless the subclass constructor writes a call to
super(...)explicitly. Reason (R): When a constructor does not begin withsuper(...), the compiler inserts a call to the superclass’s no-argument constructor. [1 mark]
- Both A and R are true and R is the correct explanation of A.
- Both A and R are true but R is not the correct explanation of A.
- A is true but R is false.
- A is false but R is true.
- Assertion (A): An abstract class cannot be used as the type of a reference variable. Reason (R): No object of an abstract class can be created. [1 mark]
- Both A and R are true and R is the correct explanation of A.
- Both A and R are true but R is not the correct explanation of A.
- A is true but R is false.
- A is false but R is true.
- Match each keyword or operator in Column A with what it does, in Column B. [4 marks]
| Column A | Column B |
|---|---|
a. extends |
i. undertakes to supply the methods an interface declares |
b. implements |
ii. reports whether an object belongs to a given class |
c. super |
iii. names the one class whose members are acquired |
d. instanceof |
iv. reaches a member of the immediate superclass |
- Fill in each blank with the correct term. [4 marks]
- A class that is not abstract is called a ______ class.
- The relationship that must hold for
B extends Ato be sound is the ______ relationship. - A variable declared inside an interface is implicitly
public,staticand ______. - Choosing the version of an overridden method while the program runs is called ______ binding.
- State whether each of the following is true or false. [4 marks]
- A private member of a superclass is present in a subclass object but cannot be used there by name.
- An abstract class must contain at least one abstract method.
- Method overloading is settled by the compiler from the arguments written at the call.
- One interface may extend another interface.
- Give the full form of ADT. [1 mark]
12.13 Check Your Concepts
Define inheritance, and give the two names by which the class that acquires the members is known. [2 marks]
What is meant by the signature of a method? State why it matters when a method is overridden. [2 marks]
Name two things a derived class does not receive from its superclass. [2 marks]
A member is declared with no access specifier at all. State where it may be used, and name one place where a
protectedmember may be used but this one may not. [2 marks]Differentiate between method overloading and method overriding, giving any three points of difference. [3 marks]
Differentiate between an abstract class and an interface, giving any three points of difference. [3 marks]
Differentiate between
==andequals()when both are applied to two object references. [3 marks]Give reasons why a constructor is not inherited by a derived class, and state how the inherited part of a subclass object is initialised instead. [3 marks]
A superclass declares an instance variable and a subclass declares another of exactly the same name. Explain with a short example what the subclass object then contains, how each of the two variables is reached from inside the subclass, and which of them is used by a method whose body was written in the superclass. [4 marks]
Explain why a superclass reference may refer to a subclass object while a subclass reference may not refer to a superclass object. State one restriction that such a superclass reference places on the programmer, and name the operator used to make it safe to remove that restriction. [4 marks]
A superclass declares only a parameterised constructor, and a subclass constructor does not begin with a call to
super(...). Explain what the compiler does in this situation, what it reports, and the two ways in which the program may be corrected. [4 marks]Explain what dynamic binding means. Using a superclass with one overridden method and two classes derived from it, show how a single statement inside a loop can produce different results for different objects. State two advantages of writing a program in this way and one honest limitation of it. [6 marks]
An abstract class and an interface both oblige other classes to supply a method. Explain, with a suitable example of each, how that obligation is created in the two cases, and state two situations in which an interface is the better choice than an abstract class. [6 marks]
12.14 Output & Debugging
- Study the following classes.
class Alpha
{
Alpha()
{
System.out.println("Alpha");
}
}
class Beta extends Alpha
{
Beta()
{
System.out.println("Beta");
}
}
class Gamma extends Beta
{
Gamma()
{
System.out.println("Gamma");
}
}Write the output produced by the statement Gamma g = new Gamma(); and explain in one line why the three lines appear in that order, although no super is written anywhere. [3 marks]
- Study the following classes.
class Reading
{
int units = 50;
void show()
{
System.out.println(units);
}
}
class Meter extends Reading
{
int units = 90;
void display()
{
System.out.println(units);
System.out.println(super.units);
}
}Write the three lines printed by Meter m = new Meter(); m.display(); m.show(); and state why the last line is what it is. [3 marks]
- Study the following classes.
class Charge
{
void print()
{
System.out.println("ordinary charge");
}
}
class LateCharge extends Charge
{
void print()
{
System.out.println("late charge");
}
}State the output of the two statements Charge c = new LateCharge(); c.print(); and name the thing that decided which version was carried out. [2 marks]
- The following classes do not compile.
interface Signal
{
String colour();
}
class StopSignal implements Signal
{
String colour()
{
return "red";
}
}Identify the error, state the rule that has been broken, and rewrite the offending line correctly. [3 marks]
- The following classes compile, but the program stops with an error while it is running.
class Fare
{
double amount()
{
return 100.0;
}
}
class NightFare extends Fare
{
double amount()
{
return amount() * 2;
}
}Name the fault, explain in one line why the program cannot finish, and rewrite the faulty statement so that a night fare of 200.0 is returned. [3 marks]
12.15 Programming Practice
A superclass
Facestores the length and the breadth of a rectangular face in decimals and has a methoddouble area()that returns length × breadth. A classCartonderived from it adds a height, and must supply a methoddouble surfaceArea()returning the total surface area of the carton, computed as 2 × (area of the face) + 2 × height × (length + breadth). The method must obtain the area of the face by calling the superclass’s method rather than repeating its formula. Assume thatFacehas protected instance variables and a parameterised constructor. Write only the classCarton, giving its constructor and itssurfaceArea()method, followed by one line explaining whysuperis needed in it. Themain()method need not be written. [4 marks]A superclass
Itemstores the details of an article sold at a school store, and a classPacketderived from it holds the number of such articles sold together.
| Class | Members |
|---|---|
Item |
name (String), price (double); a parameterised constructor; void show() to display the name and the price |
Packet |
count (int); a parameterised constructor that initialises the members of both classes; double packetPrice() returning price × count; void show() displaying the details of the item followed by the number of articles and the price of the packet |
Specify the class Item, and using the concept of inheritance specify the class Packet. Submit both classes together with a variable description table. The main() method need not be written. [6 marks]
Define an abstract class
Journeyholding a protected variable for the distance of a journey in kilometres, a parameterised constructor, an abstract methoddouble fare()and an ordinary methodvoid display()that prints the distance followed by the fare. Derive two classes from it:BusJourney, charging rupees 2.50 a kilometre, andTrainJourney, charging rupees 1.20 a kilometre together with a fixed reservation charge of rupees 40. Write the three classes and amain()method that creates one object of each kind and displays both journeys. Submit the algorithm for eachfare()method, the three classes,main(), and a variable description table. Test your program with a distance of 20 km for each kind of journey. [6 marks]Define an interface
Volumeholding the constantPIequal to 3.142 and declaring one methoddouble volume(). Write two classes that implement it:Cylinder, holding a radius and a height, whose volume is PI × radius × radius × height; andCube, holding a side, whose volume is side × side × side. Inmain(), read the measurements withScanner, store one object of each class in a single array of typeVolume, and print the two volumes using one loop. Submit the interface, both classes,main(), and a variable description table, and state in one line why the array can hold objects of two classes that are not related by inheritance. [6 marks]