Extending a class
The word extends makes one class a kind of another.
public class Fern extends Plant
That line says a Fern is a Plant. Anywhere a Plant is wanted, a Fern is accepted. A Fern starts out able to do everything a Plant can do. The department's own list of problem areas names this word: Unfamiliarity with keywords in Java required for basic things like inheritance (extends) and interface use (implements)
.
What the child gets
Write extends Plant and every field and every method declared in Plant becomes part of a Fern object. The object carries the parent's fields whether or not the child's code may read them. Call a method that Plant declared on a Fern, and it runs, with no code in Fern at all.
| From the parent | Does the child get it? |
|---|---|
| fields | yes, they are in every child object |
| public and protected methods | yes, the child can call them |
| private fields | they are in the object, and the child's code cannot use them by name |
| private methods | no; the child cannot call them |
| constructors | no; the child writes its own, and it calls the parent's (see Reaching the super class) |
A class has exactly 1 direct parent (its super class). A class that names no parent extends Object, which is where toString, equals and hashCode come from on every class.
The type of the variable against the type of the object
Plant p = new Cactus();
That line has 2 types, and they do different jobs.
| Type | On this line | What it decides | When |
|---|---|---|---|
| the variable's type | Plant | which methods you may call | when the code compiles |
| the object's type | Cactus | which method body runs | when the program runs |
So p.isIndoor() compiles, because Plant declares isIndoor. When it runs, it runs Cactus's body, because the object is a Cactus.
Now give Cactus a method that Plant does not have.
public class Cactus extends Plant
{
// constructor and isIndoor as before
public String spines()
{
return "sharp";
}
}
Plant p = new Cactus();
System.out.println(p.spines()); // does not compile
In a driver file Drive.java, where that call is line 7, the compiler says:
Drive.java:7: error: cannot find symbol
System.out.println(p.spines());
^
symbol: method spines()
The object is a Cactus, and it has a spines method. The call still fails, at compile time. The compiler reads the variable's type, Plant, and Plant declares no spines. Write Cactus c = new Cactus(); and c.spines() compiles and returns sharp.
Code written against Plant keeps working when someone adds an Orchid class later, because it only calls what every Plant declares.
A class you cannot build
There is no such thing as a plain plant. Every real one is a cactus, a fern or some other kind. Java can enforce that.
public abstract class Plant
{
private String name;
private int waterDays;
public abstract boolean isIndoor();
}
A method header with no body, marked abstract, is a promise that every subclass you can build will supply a body (an abstract method). A class that holds an abstract method has to be marked abstract too (an abstract class). Leave the word off the class line and the file does not compile:
Plant.java:1: error: Plant is not abstract and does not override abstract method isIndoor() in Plant
public class Plant
^
The abstract class line has a second effect. new Plant(...) stops compiling everywhere, with the message Plant is abstract; cannot be instantiated.
A subclass has to supply a body for every abstract method it inherits. Leave isIndoor out of Cactus and the error names Cactus, not Plant:
Cactus.java:1: error: Cactus is not abstract and does not override abstract method isIndoor() in Plant
public class Cactus extends Plant
^
The method you forgot was declared in another file, and the error is reported on the class that still owes it.
A chain longer than 2
A child can have children.
Plant
Fern extends Plant
TreeFern extends Fern
A TreeFern is a Fern, and a Fern is a Plant, so a TreeFern is a Plant too. A TreeFern can call every public method of Fern and of Plant, and a variable of type Plant holds any of the 3. Each rule above applies at each step of the chain.
In the practice APE
The Inheritance problem uses this lesson. Its header comments are the specification.
Here is the same kind of task on the Plant family. Plant is given as below. Write Succulent from its comment.
public abstract class Plant
{
private String name;
private int waterDays;
protected Plant(String name, int waterDays)
{
this.name = name;
this.waterDays = waterDays;
}
public abstract boolean isIndoor();
}
// Succulent is a Plant. It is built from a name, it is watered
// every 10 days, and it lives indoors.
Answer
public class Succulent extends Plant
{
public Succulent(String name)
{
super(name, 10);
}
@Override
public boolean isIndoor()
{
return true;
}
}
Succulent needs 3 things: extends Plant on the class line, a constructor that passes both values up to Plant's constructor, and a body for the abstract isIndoor. Leave out the third and Succulent.java does not compile.
Readiness check
Work each one on paper and write the answer down before you open it.
Plant declares isIndoor() and toString(). Cactus extends Plant and adds public String spines(), which returns "sharp". The line is Plant p = new Cactus();.
- Does
p.isIndoor()compile, and which class's body runs? - Does
p.spines()compile? If not, is the failure at compile time or at run time? - Change the line to
Cactus c = new Cactus();. Doesc.spines()compile now, and what does it return? - Does
Cactus c2 = new Plant("cactus", 14);compile?
Answers
1. It compiles, because Plant declares the method. Cactus's body runs, because the object is a Cactus.
2. It does not compile. The failure is at compile time: cannot find symbol, decided by the variable's type alone.
3. It compiles and returns sharp. The variable's type is now Cactus, which declares spines.
4. No. Plant is abstract, so the compiler stops with Plant is abstract; cannot be instantiated. A Plant is also not a Cactus, so it could not go in that variable even if it could be built.
Start from a working version. Plant is abstract, with 2 private fields, a protected constructor and a toString. Fern extends Plant, its constructor calls super(name, 3), and it implements isIndoor with @Override above it. A driver writes Plant p2 = new Fern("Boston fern");.
- Delete
extends PlantfromFern. Name the 3 lines that now fail to compile. - Put
extends Plantback and deleteFern'sisIndoormethod. What fails, and which class does the error name? - Put
isIndoorback and delete the wordabstractfromPlant's class line, leaving the abstract method in place. What fails?
Answers
1. The super(name, 3) call, because Fern's parent is now Object, which has no constructor taking a String and an int. The @Override on isIndoor, because no parent declares that method now. The driver line, because a Fern is no longer a Plant.
2. Fern fails with Fern is not abstract and does not override abstract method isIndoor() in Plant. The error names Fern, even though the method was declared in Plant.
3. Plant.java fails with Plant is not abstract and does not override abstract method isIndoor() in Plant. A class that holds a method with no body has to be abstract.
Plant is abstract, holds private String name and private int waterDays, and has a public getName(). Fern extends Plant and adds private boolean shade and a public needsShade(). TreeFern extends Fern and adds private int height.
- How many fields does 1
TreeFernobject carry, counting every level? - Can
TreeFern's code readnamedirectly? Can it callneedsShade()? Can it callgetName()? - Is
Plant p = new TreeFern();legal? IsFern f = new TreeFern();legal? - Is
TreeFern t = new Fern("Boston fern");legal?
Answers
1. 4: name and waterDays from Plant, shade from Fern, and height from TreeFern.
2. It cannot read name, which is private to Plant. It can call needsShade() and getName(), which are public. Being in the object and being reachable by name are different questions.
3. Both are legal. A TreeFern is a Fern, and a Fern is a Plant.
4. No. A Fern is not a TreeFern. The "is a" relation runs from the child up, never down.
Further reading
- Object (Java SE 19 & JDK 19). The class at the top of every chain, including the ones that name no parent.
- Lesson: Interfaces and Inheritance (The Java Tutorials). Oracle's trail for this lesson and the next 3.
- Inheritance (The Java Tutorials). What a subclass inherits and what it does not.
- Abstract Methods and Classes (The Java Tutorials). Why the class has to carry the word when a method does.
- Object as a Superclass (The Java Tutorials). The methods every class has before you write any.
- Polymorphism (The Java Tutorials). The variable type against the object type, worked on a small hierarchy.
- Inheritance (object-oriented programming) (Wikipedia). The idea across languages.
- Liskov substitution principle (Wikipedia). What "is a" has to mean for substituting the child to be safe.
- Reading 10: Abstract Data Types, from MIT 6.031, Software Construction. Where a type's operations come from.
- Chapter 8. Classes (The Java Language Specification, Java SE 19). Section 8.1.4 is the
extendsclause.