Accessing super class methods and fields
A child object carries the parent's fields inside it. The child's code is still not allowed to touch all of them. Inheriting a field and being allowed to read it are 2 separate questions. The parent decides the second one by leaving some members open to its subclasses and keeping the rest shut.
Every example below uses 1 small family of classes. Plant is the parent. Cactus and Fern extend it.
Private is private to the class, not to the family
public abstract class Plant
{
private String name;
private int waterDays;
}
public class Fern extends Plant
{
public Fern(String name)
{
this.name = name; // does not compile
}
}
The compiler stops on that line:
Fern.java:5: error: name has private access in Plant
this.name = name; // does not compile
^
A Fern object really does hold a name field. The line still fails. private means private to the class that declared the field, and Fern is a different class in a different file.
The reason is the guarantee. Plant made the fields private so that only Plant's own code can change them. A subclass written next year would otherwise be a way around that guarantee. So the fields stay shut, and the parent offers specific ways in.
The 4 levels, and the one that means family
| Modifier | Who can reach it |
|---|---|
private | only code inside the same class |
| none written | any class in the same package |
protected | any class in the same package, plus subclasses anywhere |
public | anybody |
protected exists for this situation. A protected member is left out of the class's public offer and is open to the classes that extend it. A protected constructor is the common case. Nobody should build a plain Plant, and every subclass has to be able to call its constructor.
public abstract class Plant
{
private String name;
private int waterDays;
protected Plant(String name, int waterDays)
{
this.name = name;
this.waterDays = waterDays;
}
}
The fields are still shut, and there is exactly 1 way to fill them. Make that constructor private and no subclass can call it, so the subclasses do not compile. Make it public and code outside the family can call it too. That compiles, and it opens more than the parent needed to open.
super(...) and where it goes
A child constructor reaches the parent's constructor with super, written as a call, with the arguments the parent's constructor takes.
public class Fern extends Plant
{
public Fern(String name)
{
super(name, 3);
}
}
public class Cactus extends Plant
{
public Cactus()
{
super("cactus", 14);
}
}
Fern passes up the name it was given and a fixed 3. Cactus takes no arguments and passes up 2 fixed values. The order of the arguments is the order of the parameters on Plant's constructor.
Put the super call first in the constructor. One object is being built, and the parent's half has to be filled in before the child's code uses it.
| Java version | A statement above super(...) |
|---|---|
| 24 and earlier | does not compile |
| 25 and later | compiles, when the statement does not use the object being built (a check on an argument, or a print) |
The APE does not say which Java version its machines run, and the API documentation it links is Java SE 19. Write super(...) as the first statement and the constructor compiles on every version.
Java also fills the call in when you leave it out. With no super call, the compiler inserts super(), with no arguments, at the top of the constructor. Plant's only constructor takes a String and an int, so the inserted call has nothing to reach:
Fern.java:4: error: constructor Plant in class Plant cannot be applied to given types;
{
^
required: String,int
found: no arguments
reason: actual and formal argument lists differ in length
The error points at the opening brace of a constructor where you wrote no call at all. Read it as: Java guessed super(), and Plant has no constructor that takes nothing.
A constructor may call this(...) instead, which runs another constructor of the same class. That is how a class with 2 constructors keeps its real setup in 1 of them. Following the chain of this(...) calls always ends at a super(...) call before any child body runs.
Going up for a method instead of a field
super also names the parent's version of a method you overrode.
@Override
public String toString()
{
return super.toString() + ", keep out of direct sun";
}
Inside Fern, that prints Boston fern, water every 3 days, keep out of direct sun for a fern named Boston fern. The first part comes from Plant's toString, which can read the private fields because it is Plant's own code.
Without the word super, toString() means Fern's own method, and the method calls itself until the program stops with a StackOverflowError. With it, the call goes to the version Fern replaced.
Fern never reads name or waterDays to build that string. It asks the parent for the string.
What the parent hands you
| Way in | What it is for |
|---|---|
a protected or public constructor, called as super(...) | setting the parent's fields when the object is built |
the parent's public and protected methods, such as getName() | reading and changing the parent's state afterward |
super.method() | running the parent's version of a method you overrode |
Direct access to a private field is not on the list.
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. The comments are the specification, and you write the 2 constructors.
// Plant: the constructor sets both fields. It may be called only by
// subclasses of Plant and by classes in the same package.
____ Plant(String name, int waterDays)
// Orchid extends Plant. An Orchid is built from a name alone.
// Every orchid is watered every 7 days.
public Orchid(String name)
Answer
protected Plant(String name, int waterDays)
{
this.name = name;
this.waterDays = waterDays;
}
public Orchid(String name)
{
super(name, 7);
}
"Only by subclasses and by classes in the same package" is the row for protected in the table above. Orchid cannot assign the private fields, so it passes both values up, in the order Plant's parameters list them.
Readiness check
Work each one on paper and write the answer down before you open it.
Plant is abstract, has 2 private fields, a protected 2-argument constructor, and a public toString. Fern extends Plant.
- Inside
Fern's constructor:this.waterDays = 3;Does it compile? - Inside
Fern's constructor:super(name, 3);as the first statement. Does it compile? - Inside
Fern, a method writesreturn super.toString();Does it compile? - In another package, in a class that does not extend
Plant, code writesnew Plant("ivy", 5);Does it compile, and how many reasons are there?
Answers
1. No. waterDays is private to Plant, and Fern is a different class.
2. Yes. That is the parent's way in, called first.
3. Yes. toString is public on Plant, and super. names the parent's version.
4. No, for 2 reasons, and the compiler reports only the first: Plant is abstract; cannot be instantiated. The constructor is protected, so a class outside the package that is not a subclass cannot call it. And Plant is abstract, so new Plant(...) is illegal everywhere.
Plant's only constructor is protected Plant(String name, int waterDays).
Fern's constructor has nosupercall at all. What happens, and what does the error message mention?Plantis given a second constructor that takes no arguments. Now what happens to the constructor from question 1?Fern's constructor starts withSystem.out.println("building");and then callssuper(name, 3);. What happens under Java 21, and what happens under Java 25?Fernhas 2 constructors, and one of them starts withthis("Boston fern");. Is that legal?
Answers
1. It does not compile. Java inserted super(), and Plant has no constructor that takes no arguments. The message says required: String,int and found: no arguments.
2. It compiles now. The inserted super() calls the new empty constructor, so the fields keep their default values: null for the name and 0 for the days. Nothing warns you that the 2 values never arrived, which is the reason to write the call yourself.
3. Under Java 21 it does not compile, because the call to super must be the first statement. Under Java 25 it compiles and prints building before Plant's constructor runs, because the print does not use the object being built.
4. Yes. The chain of this(...) calls reaches a super(...) call before any body runs.
A chain of 3 classes. Each constructor prints 1 line. Plant's constructor also prints what isIndoor() returns while the object is being built.
public abstract class Plant
{
private String name;
protected Plant(String name)
{
System.out.println("plant");
System.out.println("indoor during build: " + this.isIndoor());
this.name = name;
}
public abstract boolean isIndoor();
}
public class Fern extends Plant
{
public Fern(String name)
{
super(name);
System.out.println("fern");
}
@Override
public boolean isIndoor()
{
return true;
}
}
public class TreeFern extends Fern
{
private boolean outside = true;
public TreeFern()
{
super("tree fern");
System.out.println("tree fern");
}
@Override
public boolean isIndoor()
{
return !this.outside;
}
}
- What does
new TreeFern();print, in order? - Which constructor's body finishes first, and which finishes last?
- After the object is built, what does its
isIndoor()return, and why did the line printed during the build say something different?
Answers
1. 4 lines: plant, indoor during build: true, fern, tree fern.
2. Plant's body finishes first and TreeFern's finishes last. The super calls go up the chain, and the bodies run on the way back down.
3. It returns false. The object is a TreeFern from the start, so the call inside Plant's constructor ran TreeFern's isIndoor. At that moment outside still held its default value, false, because TreeFern's own field setup runs after the super call returns. So !this.outside was true during the build.
Further reading
- Using the Keyword super (The Java Tutorials). Both uses on 1 page: the constructor call and the method call.
- Controlling Access to Members of a Class (The Java Tutorials). Oracle's table of who can see what, including the row for protected.
- Inheritance (The Java Tutorials). What a subclass can and cannot do with what it inherits.
- Abstract Methods and Classes (The Java Tutorials). Why the parent in these problems is usually not a class you build.
- Providing Constructors for Your Classes (The Java Tutorials). What Java writes for you when you write no constructor.
- JEP 513: Flexible Constructor Bodies. The Java 25 change that allows some statements above
super(...). - Object (Java SE 19 & JDK 19). The end of every chain of
supercalls. - Constructor (object-oriented programming) (Wikipedia). Constructor chaining across languages.
- Encapsulation (computer programming) (Wikipedia). Why the field stays shut to the child.
- Lecture 5: Access Control, Class Scope, Packages, Java API, MIT 6.092, on MIT OpenCourseWare. Access levels worked through in slides.
- 8.8.7.1 Explicit Constructor Invocations (The Java Language Specification, Java SE 19). The rule about the first statement as Java 19 states it.