← Data abstraction CS Skill Tree

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 parentDoes the child get it?
fieldsyes, they are in every child object
public and protected methodsyes, the child can call them
private fieldsthey are in the object, and the child's code cannot use them by name
private methodsno; the child cannot call them
constructorsno; 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.

TypeOn this lineWhat it decidesWhen
the variable's typePlantwhich methods you may callwhen the code compiles
the object's typeCactuswhich method body runswhen 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.

The subclass that forgot to implement

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.

TraceWhat the variable lets you call

Plant declares isIndoor() and toString(). Cactus extends Plant and adds public String spines(), which returns "sharp". The line is Plant p = new Cactus();.

  1. Does p.isIndoor() compile, and which class's body runs?
  2. Does p.spines() compile? If not, is the failure at compile time or at run time?
  3. Change the line to Cactus c = new Cactus();. Does c.spines() compile now, and what does it return?
  4. 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.

TraceTake the word out

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");.

  1. Delete extends Plant from Fern. Name the 3 lines that now fail to compile.
  2. Put extends Plant back and delete Fern's isIndoor method. What fails, and which class does the error name?
  3. Put isIndoor back and delete the word abstract from Plant'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.

Trace2 links up

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.

  1. How many fields does 1 TreeFern object carry, counting every level?
  2. Can TreeFern's code read name directly? Can it call needsShade()? Can it call getName()?
  3. Is Plant p = new TreeFern(); legal? Is Fern f = new TreeFern(); legal?
  4. 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