الدوال البانية وsuper
في هذا الدرس 11 موضوعات
هذا الدرس متاح حاليًا باللغة الإنجليزية فقط، ولم تُنشر نسخته العربية بعد.
Lesson Overview
In the last lesson you used super(owner, balance) once, on trust. This lesson explains it. A SavingsAccount object is not built from nothing: it contains the whole Account part (the owner and the balance) plus its own part (the rate). Somebody has to build the parent part, and the language makes sure it happens first.
We continue with the family tree: a child cannot exist without its parent, so the parent is always "born" first. The tool for talking to the parent is the keyword super.
Why this matters: forgetting super(...) is the most common compile error when beginners start with inheritance. Knowing the rule turns a confusing message into a one-line fix.
This lesson builds on "Reusing behavior with extends" and on constructors and this(...) from the previous module.
Learning Objectives
By the end of this lesson you will be able to:
- Explain why constructors are not inherited.
- Write a subclass constructor that calls a superclass constructor with
super(...). - State where
super(...)must appear and what Java inserts when you write nothing. - Predict the order in which the parts of an object are built.
- Read and fix the compiler error caused by a missing
super(...)call. - Call the parent's version of a method with
super.method().
Written Explanation
Constructors belong to one class
A constructor's job is to put a new object into a valid first state. Account(String owner, double balance) knows how to do that for an Account. A SavingsAccount has an extra field, rate, which the Account constructor knows nothing about. So constructors are not inherited: each class writes its own.
But the parent part still has to be set up properly, with the parent's rules. The subclass constructor therefore asks the parent's constructor to do it.
super(...): calling the parent constructor
Inside a subclass constructor, super(arguments) calls a constructor of the direct superclass:
SavingsAccount(String owner, double balance, double rate) {
super(owner, balance); // build the Account part
this.rate = rate; // then build the SavingsAccount part
}The arguments decide which parent constructor runs, exactly as with new: super(owner, balance) matches Account(String, double).
The Java tutorial states the key rule: a call to a superclass constructor must be the first line of the subclass constructor. Putting anything before it is a compile-time error. This guarantees the parent part exists before the child part uses it.
The order of construction
When you create a Puppy that extends Dog that extends Animal, the constructors run from the top of the chain down. The tutorial describes this as constructor chaining that goes up the hierarchy until it reaches Object's constructor; what that means for you is: parent first, child last.
class Animal {
Animal() { System.out.println("1. Animal part built"); }
}
class Dog extends Animal {
Dog() { System.out.println("2. Dog part built"); }
}
class Puppy extends Dog {
Puppy() { System.out.println("3. Puppy part built"); }
}new Puppy() prints:
1. Animal part built
2. Dog part built
3. Puppy part builtNotice that Dog() and Puppy() never wrote super(). Java did it for them.
What Java inserts when you write nothing
If a constructor does not start with super(...) (or this(...)), the compiler inserts a call to the parent's no-argument constructor, super(). Two cases follow:
- The parent has a no-argument constructor (a class with no constructors at all gets a default one): everything works silently, as in the
Puppyexample. - The parent has constructors but none without parameters: the implicit
super()has nothing to call, and the compiler reports an error.
Here is case 2. Account only has Account(String, double), and the subclass constructor forgets super(...):
class SavingsAccount extends Account {
private double rate;
SavingsAccount(String owner, double rate) {
this.rate = rate;
}
}Compiling with JDK 21 gives:
Main.java:14: error: constructor Account in class Account cannot be applied to given types;
SavingsAccount(String owner, double rate) {
^
required: String,double
found: no arguments
reason: actual and formal argument lists differ in lengthRead it as: "you are building an Account, and you did not tell it the String and double it requires." The fix is to add super(owner, 0); (or take a balance parameter) as the first line.
The same message appears when a class has no constructor at all but its parent has no no-argument constructor, because the compiler-provided default constructor also calls super().
Constructors can still chain with this(...)
From the previous module you know this(...) calls another constructor of the same class. It also must be the first statement, so a constructor uses either this(...) or super(...), not both. The chain ends at a constructor that calls super(...):
SavingsAccount(String owner, double balance, double rate) {
super(owner, balance);
this.rate = rate;
}
SavingsAccount(String owner, double rate) {
this(owner, 0, rate); // reuse the constructor above
}super.method(): the parent's version
super followed by a dot and a method name calls the parent's version of a method. You use it when the child wants to add to the parent's behavior instead of replacing it. Suppose Account has describe() and SavingsAccount declares its own describe() with the same signature:
String describe() {
return super.describe() + " (rate " + rate + ")";
}This is called overriding, the subject of the next lesson. Here, just see the tool: super.describe() runs the code written in Account, then the child appends its own text.
A fine point to keep: super(...) with parentheses is a constructor call that can appear only as the first statement of a constructor. super.name(...) with a dot is a method call that can appear anywhere in an instance method.
A note about Java versions
The "first statement" rule is the rule in the Java versions this course uses. Newer Java releases have been relaxing what may appear before super(...). Do not rely on that in beginner code: keep super(...) as the first line.
Examples
Example 1: Animal and Dog with a name (simple)
class Animal {
private final String name;
Animal(String name) {
this.name = name;
}
String getName() { return name; }
}
class Dog extends Animal {
Dog(String name) {
super(name);
}
}
public class Main {
public static void main(String[] args) {
Dog d = new Dog("Rex");
System.out.println(d.getName());
}
}Output:
RexDog has no fields of its own, but its constructor still has to pass the name to Animal. name stays private in Animal: Dog never sees the field, only the parent's code does.
Example 2: construction order (practical)
The Animal, Dog, Puppy classes above prove the order: output lines 1, 2, 3, parent before child. This matters in practice because a child constructor can safely use anything the parent set up, since it is already finished. It also means you should not call a method from a parent constructor and expect child data to be ready, a trap Lesson 4 returns to.
Example 3: SavingsAccount with two constructors (realistic)
class Account {
private final String owner;
private double balance;
Account(String owner, double balance) {
this.owner = owner;
this.balance = balance;
}
String describe() {
return owner + ": " + balance;
}
}
class SavingsAccount extends Account {
private final double rate;
SavingsAccount(String owner, double balance, double rate) {
super(owner, balance);
this.rate = rate;
}
SavingsAccount(String owner, double rate) {
this(owner, 0, rate);
}
String describe() {
return super.describe() + " (rate " + rate + ")";
}
}
public class Main {
public static void main(String[] args) {
System.out.println(new SavingsAccount("Sara", 500, 0.05).describe());
System.out.println(new SavingsAccount("Omar", 0.03).describe());
}
}Output:
Sara: 500.0 (rate 0.05)
Omar: 0.0 (rate 0.03)The two-argument constructor starts accounts at zero by delegating with this(...), which in turn calls super(...). In the next lesson you will add @Override to describe(); here it works without, which is exactly the habit to fix.
Backend Insight
Real backend classes validate in their constructors: an Account may refuse a negative starting balance. Because a subclass constructor must call a parent constructor first, that validation always runs, and the subclass cannot create a half-built parent part. This is how a base class protects its invariants even from its own children.
You will also meet this in frameworks: a class you write may have to call super(...) with configuration the framework base class needs. The compiler error above is what you will see if you forget it. You can now read it in seconds.
Common Mistakes
Mistake 1: Forgetting super(...) when the parent has no no-argument constructor
You saw the error above. Add super(...) as the first line with arguments the parent's constructor accepts.
Mistake 2: Putting statements before super(...)
Mid() {
System.out.println("hi");
super(1);
}error: call to super must be first statement in constructorMove super(...) to the first line. Do any preparation inside the arguments, for example super(name.trim()).
Mistake 3: Using both this(...) and super(...)
Each must be first, so one constructor can have only one. Let this(...) delegate to a constructor that calls super(...).
Mistake 4: Assigning the parent's private field in the child
this.owner = owner; in SavingsAccount fails because owner is private in Account. Pass the value to super(...) instead.
Mistake 5: Confusing super(...) with super.method()
Parentheses directly after super call a constructor, dot-name calls a method.
Exercises
Try each exercise yourself before opening the solutions.
Level A: Foundation
A1. What does the compiler insert at the start of a subclass constructor that does not begin with super(...) or this(...)?
أظهر الحل A1
A1. A call to the parent's no-argument constructor, super();.
A2. class Parent { Parent(int x) { } } and class Child extends Parent { Child() { } }. Does Child compile? Why or why not?
أظهر الحل A2
A2. No. Child() starts with an implicit super(), but Parent only has Parent(int). The compiler reports that the constructor cannot be applied to the given types.
A3. With class A { A() { print("A"); } } and class B extends A { B() { print("B"); } } (where print stands for System.out.println), what does new B() print, in order?
أظهر الحل A3
A3. A then B: the parent part first, then the child.
Level B: Application
B1. Write Book with a private final title set by its constructor and a getTitle(). Write EBook extends Book with a private final sizeMb and an info() method returning title (sizeMb MB). Add a second EBook constructor taking only the title that defaults the size to 1.
أظهر الحل B1
B1.
class Book {
private final String title;
Book(String title) { this.title = title; }
String getTitle() { return title; }
}
class EBook extends Book {
private final int sizeMb;
EBook(String title, int sizeMb) {
super(title);
this.sizeMb = sizeMb;
}
EBook(String title) {
this(title, 1);
}
String info() { return getTitle() + " (" + sizeMb + " MB)"; }
}new EBook("Java").info() gives Java (1 MB) and new EBook("Java", 5).info() gives Java (5 MB).
B2. Fix this class. Do not change Vehicle.
class Vehicle { Vehicle(String plate) { } }
class Car extends Vehicle {
Car() { }
}أظهر الحل B2
B2. Give Car a plate to pass up, for example Car(String plate) { super(plate); }, or call super("unknown") in the existing no-argument constructor.
B3. With A and B extended to have A(String s) and B(String s) { super(s); ... }, explain what new B("x") does first.
أظهر الحل B3
B3. It first runs A(String) with "x", because B's constructor starts with super(s). Then the rest of B's constructor body runs. Output: A with x then B with x.
Level C: Challenge
C1. Three classes Animal(String name), Dog(String name), Puppy(String name) each print their class name in the constructor after calling super(name). What is printed for new Puppy("Bo")? Where would you put a print so that "starting" appears before everything?
أظهر الحل C1
C1. Parent first, child last: Animal, Dog, Puppy, each printing its own name. A "starting" message cannot go inside the constructors because super(...) must come first. Print it in main before new Puppy("Bo"), or inside an argument expression passed to super.
C2. A colleague says: "super(...) just copies the parent's fields into the child." Correct this in two sentences.
أظهر الحل C2
C2. super(...) does not copy fields; it runs a constructor of the parent class that sets up the parent part of the same single object. There is one object, built in layers, not two objects.
Mini Challenge
Write Person with a private name and a constructor that rejects an empty name by printing Invalid name and storing "unknown" instead. Write Student extends Person with a school field. Give Student a constructor (name, school) using super(name). Create new Student("", "Cairo University") and print the stored name. Then delete the super(name) line, compile, and read the error.
Check your work: the validation inside Person ran even though you constructed a Student, and the compiler explained the missing arguments.
Summary & Next Lesson
Key takeaways
- Constructors are not inherited; every class writes its own.
- A subclass constructor starts with
super(...)(orthis(...)). If you write nothing, Java insertssuper(). - If the parent has no no-argument constructor, you must write
super(...)yourself. - Objects are built parent first, child last.
super.method()calls the parent's version of a method; it is different fromsuper(...).
Next lesson
The next lesson, "Overriding methods", shows how a subclass replaces a parent's behavior, and why @Override is worth typing every time.
Self-check
- Why are constructors not inherited?
- What must be the first statement of a subclass constructor that has no
this(...)? - When does the implicit
super()cause a compile error? - In what order are the constructors of a three-level chain run?
- What does
super.describe()do?
أظهر الإجابات
- A constructor builds one specific class, so each class writes its own.
- A call
super(...). If you omit it, Java insertssuper(). - When the parent has no accessible no-argument constructor.
- From the top parent down to the child.
- It runs the parent's version of
describe().
References
- Oracle, Java Tutorials, Using the Keyword super. Supports:
super(...)must be the first line of the constructor, the compiler inserts a no-argument call when you omit it, the error when none exists, and constructor chaining up toObject. - Oracle, Java Language Specification SE 21, Chapter 8, Classes. Consulted for the single-superclass rule; I did not read the full constructor section, so the note about newer Java versions relaxing the first-statement rule is from general knowledge and is hedged in the text.
- The error messages and outputs above were produced by compiling and running the code with JDK 21.
التطبيق
طبّق هذا الدرس بمختبر الوحدة وتمارينها واختبارها.
تعليم الدرس كمكتمل اختياري ويمكنك التراجع عنه في أي وقت. يبقى تقدمك محفوظًا في هذا المتصفح.