Lesson 52 / 140
লিখিত lesson · 16 মিনিট

Parent Construction, `super`, and Inherited Access

Initialize the parent part of a child object with `super(...)`, predict constructor order, and understand `private`, `protected` and `final` members.

Preview

আপনি একটি free preview lesson দেখছেন।

Lesson Overview

When a child object is created, it is not only the child class's state that is initialized. Its parent portion must also be initialized into a valid state. Consider:

java
VideoLesson videoLesson = new VideoLesson(1L, "Inheritance Basics",
                "https://cdn.tarkev.com/videos/inheritance",
                18);

A VideoLesson is a ContentItem. Conceptually, the object's state is:

text
ContentItem state
→ id
→ title
→ published

VideoLesson state
→ videoUrl
→ durationInMinutes

Therefore, it is not enough for the VideoLesson constructor to initialize only the video-specific fields. The parent state must also be initialized, and Java uses super(...) for this purpose. This lesson follows a child object through construction and then looks at what the child may and may not touch in its parent: private state, protected members, and final methods and classes.

Learning Objectives

After completing this lesson, you will be able to:

  • Explain why the parent constructor is required during child-object creation
  • Call a parent constructor using super(...)
  • Understand when automatic super() works
  • Predict constructor execution order
  • Distinguish between this(...) and super(...)
  • Understand access rules for parent private members
  • Explain the basic purpose of protected
  • Understand the effects of a final method and a final class

Parent State Must Be Initialized

Take the ContentItem and VideoLesson from the previous lesson. The parent's constructor validates and assigns its private final fields id and title; before we write its constructor, the child only adds its own fields:

java
public class VideoLesson extends ContentItem {

    private final String videoUrl;
    private final int durationInMinutes;
}

A VideoLesson object also contains the parent-defined state. Therefore, the parent constructor must execute.

Calling the Parent Constructor

To call the parent constructor from a child constructor, use super(...):

java
public class VideoLesson extends ContentItem {

    private final String videoUrl;
    private final int durationInMinutes;

    public VideoLesson(
            long id, String title, String videoUrl, int durationInMinutes) {
        super(id, title);

        this.videoUrl = videoUrl;
        this.durationInMinutes = durationInMinutes;
    }
}

Here, super(id, title); calls the parent constructor ContentItem(long id, String title).

Why Is super(...) Necessary?

The parent fields id and title are declared private final in ContentItem, and the child cannot directly assign them. This is invalid:

java
public VideoLesson(long id, String title) {
    this.id = id;
    this.title = title;
}

because id and title are private fields of the parent. javac rejects it with error: id has private access in ContentItem (and the same for title). The correct approach is super(id, title);: the parent initializes its own state.

Parent Validation Is Reused

The parent constructor already validates id and title, so the child constructor does not need to repeat the same validation:

java
public VideoLesson(
        long id, String title, String videoUrl, int durationInMinutes) {
    super(id, title);

    // validate video-specific state
}

Responsibility:

text
ContentItem
→ validates parent state

VideoLesson
→ validates child-specific state

This reduces duplication and centralizes the rules.

Constructor Execution Order

Suppose the hierarchy is:

Diagram আঁকা হচ্ছে…

When new VideoLesson(...) is executed, construction proceeds from parent toward child: the ContentItem constructor finishes before the rest of the VideoLesson constructor runs.

Observing the Order

java
public class ContentItem {

    public ContentItem(long id, String title) {
        System.out.println("ContentItem constructor");
    }
}
java
public class VideoLesson extends ContentItem {

    public VideoLesson(long id, String title) {
        super(id, title);

        System.out.println("VideoLesson constructor");
    }
}

Usage:

java
new VideoLesson(1L, "Inheritance");

Output:

text
ContentItem constructor
VideoLesson constructor

Parent Initializes First

It is important that parent state is initialized first. After super(id, title);, the child constructor can safely use parent methods:

java
public VideoLesson(long id, String title) {
    super(id, title);
    System.out.println(getTitle());
}

By this point, the parent constructor has already initialized title.

super(...) Must Be First

Wrong:

java
public VideoLesson(long id, String title) {
    System.out.println("Creating video");
    super(id, title);
}

This will not compile:

text
error: call to super must be first statement in constructor

Correct:

java
public VideoLesson(long id, String title) {
    super(id, title);
    System.out.println("Creating video");
}

A constructor-chaining call must be the first statement. Java 25 relaxed this rule (JEP 513, flexible constructor bodies): statements that do not use the object being built may come before super(...). This course targets Java 21, where the call must come first.

Automatic super()

If a child constructor does not explicitly write super(...) (or this(...)), the compiler inserts a call to super();. Example parent:

java
public class ContentItem {

    public ContentItem() {
    }
}

Child:

java
public class VideoLesson extends ContentItem {

    public VideoLesson() {
    }
}

The compiler treats the child constructor as if it were written like this:

java
public VideoLesson() {
    super();
}

When Automatic super() Fails

Parent:

java
public class ContentItem {

    public ContentItem(long id, String title) {
    }
}

There is no ContentItem() constructor. Child:

java
public class VideoLesson extends ContentItem {

    public VideoLesson() {
    }
}

The compiler tries super();, but no matching parent constructor exists. The result is a compile error. javac reports:

text
error: constructor ContentItem in class ContentItem cannot be applied to given types;
  required: long,String
  found:    no arguments
  reason: actual and formal argument lists differ in length

The child must explicitly call a valid parent constructor:

java
public VideoLesson(long id, String title) {
    super(id, title);
}

Do Not Weaken Parent Invariants

A poor fix would be to add an empty parent constructor just so that the child can compile:

java
public ContentItem() {
}

If the parent requires a valid id and a valid title, an empty constructor could allow invalid parent state. Prefer super(id, title); and keep the parent's rules intact.

Constructors Are Not Inherited

The parent has:

java
public ContentItem(long id, String title) {
}

That does not automatically give the child a VideoLesson(long id, String title) constructor. The child must declare its own constructor:

java
public VideoLesson(long id, String title) {
    super(id, title);
}

Methods can be inherited. Constructors are not inherited.

Child Can Require Additional State

The parent requires id and title. A video lesson additionally requires videoUrl and durationInMinutes. Complete constructor:

java
public VideoLesson(
        long id, String title, String videoUrl, int durationInMinutes) {
    super(id, title);

    if (videoUrl == null || videoUrl.isBlank()) {
        throw new IllegalArgumentException("Video URL is required.");
    }

    if (durationInMinutes <= 0) {
        throw new IllegalArgumentException(
                "Video duration must be positive.");
    }

    this.videoUrl = videoUrl.strip();
    this.durationInMinutes = durationInMinutes;
}

Initialization responsibility remains clear.

this(...) vs. super(...)

We previously learned that this(...) calls another constructor in the same class. super(...) calls a constructor in the parent class.

SyntaxCalls
this(...)Same-class constructor
super(...)Parent-class constructor

You Cannot Directly Use Both

A constructor can have only one first statement. Invalid (it does not compile):

java
public VideoLesson(long id, String title) {
    this(id, title, "https://example.com", 1);
    super(id, title);
}

javac reports error: call to super must be first statement in constructor. If a constructor uses this(...), the delegated constructor must eventually call super(...).

Valid Constructor Chaining

Inside VideoLesson, the shorter constructor delegates to the full one, and only the full one calls the parent:

java
public VideoLesson(long id, String title, String videoUrl) {
    this(id, title, videoUrl, 1);
}

public VideoLesson(
        long id, String title, String videoUrl, int durationInMinutes) {
    super(id, title);
    // validate and assign videoUrl and durationInMinutes as above
}

Flow:

Diagram আঁকা হচ্ছে…

Parent private State

If the parent declares private final String title;, the child cannot directly write:

java
System.out.println(title);

because title is private to ContentItem. javac reports error: title has private access in ContentItem.

Access Parent State Through Methods

Parent:

java
public String getTitle() {
    return title;
}

Child:

java
public void printTitle() {
    System.out.println(getTitle());
}

This works, and parent state remains encapsulated.

Private State Still Belongs to the Parent Portion

A VideoLesson object contains the state initialized by ContentItem, but only ContentItem code can touch its private fields. The parent owns the field, exposes controlled behavior, and the child (or any caller) uses that behavior. When we call videoLesson.publish() or videoLesson.isPublished(), the inherited public methods read and change the parent's private published field; the child never needs direct field access.

The protected Modifier

A protected member is accessible to subclasses, and to every class in the same package. Example:

java
protected String contentTypeLabel() {
    return "Content";
}

Child:

java
public class VideoLesson extends ContentItem {

    public void printType() {
        System.out.println(contentTypeLabel());
    }
}

This can be useful when the parent deliberately exposes behavior for subclasses.

Avoid protected Fields by Default

Technically, protected String title; allows direct field access from the child. Then the child may write:

java
title = "";

and bypass parent validation. This weakens encapsulation. When possible, prefer:

text
private fields
+
public/protected methods

protected Should Be Deliberate

When you mark something protected, you are effectively saying:

Subclasses are allowed to depend on this member.

So use it intentionally—not simply because child code needs convenient access.

Package-Private vs. protected

A member with no modifier is package-private:

java
void validate() {
}

A subclass in another package cannot directly access it. protected is different: it includes package access, and subclasses in other packages can also use the member. The exact cross-package rules become more relevant in larger inheritance hierarchies. For now:

text
package-private
→ package collaboration

protected
→ subclass-oriented access

final Method

A parent method marked final cannot be overridden by child classes:

java
public final long getId() {
    return id;
}

The child cannot redefine it:

java
@Override
public long getId() {
    return 999L;
}

This is a compile error:

text
error: getId() in VideoLesson cannot override getId() in ContentItem
  overridden method is final

Why Use a final Method?

Use final when a specific inherited behavior must remain unchanged. For example, making getId() final might preserve a stable identity contract. Do not mechanically make every method final. Use it where preventing specialization is intentional.

final Class

A final class cannot be extended:

java
public final class CourseCode {

}

Invalid:

java
public class SpecialCourseCode extends CourseCode {

}

javac reports error: cannot inherit from final CourseCode.

Why Make a Class final?

It is useful when inheritance does not make sense, when immutable value behavior must stay fixed, or when subclass specialization is undesirable. Our earlier CourseCode value object is a good example.

Complete Example

ContentItem.java

java
public class ContentItem {

    private final long id;
    private final String title;

    private boolean published;

    public ContentItem(long id, String title) {
        if (id <= 0) {
            throw new IllegalArgumentException("Content ID must be positive.");
        }

        if (title == null || title.isBlank()) {
            throw new IllegalArgumentException("Content title is required.");
        }

        this.id = id;
        this.title = title.strip();
        this.published = false;
    }

    public final long getId() {
        return id;
    }

    public final String getTitle() {
        return title;
    }

    public final boolean isPublished() {
        return published;
    }

    public boolean publish() {
        if (published) {
            return false;
        }

        published = true;
        return true;
    }
}

VideoLesson.java

java
public class VideoLesson extends ContentItem {

    private final String videoUrl;
    private final int durationInMinutes;

    public VideoLesson(long id, String title, String videoUrl) {
        this(id, title, videoUrl, 1);
    }

    public VideoLesson(
            long id, String title, String videoUrl, int durationInMinutes) {
        super(id, title);

        if (videoUrl == null || videoUrl.isBlank()) {
            throw new IllegalArgumentException("Video URL is required.");
        }

        if (durationInMinutes <= 0) {
            throw new IllegalArgumentException(
                    "Video duration must be positive.");
        }

        this.videoUrl = videoUrl.strip();
        this.durationInMinutes = durationInMinutes;
    }

    public String getVideoUrl() {
        return videoUrl;
    }

    public int getDurationInMinutes() {
        return durationInMinutes;
    }
}

Main.java

java
public class Main {

    public static void main(String[] args) {
        VideoLesson videoLesson = new VideoLesson(1L, "Parent Construction",
                        "https://cdn.tarkev.com/videos/super",
                        15);

        videoLesson.publish();

        System.out.println(videoLesson.getId());
        System.out.println(videoLesson.getTitle());
        System.out.println(videoLesson.isPublished());
        System.out.println(videoLesson.getVideoUrl());
    }
}

Output:

text
1
Parent Construction
true
https://cdn.tarkev.com/videos/super

Construction Flow

For new VideoLesson(1L, "Parent Construction", "https://cdn.tarkev.com/videos/super", 15), the high-level flow is:

text
1. VideoLesson constructor selected
2. super(id, title) called
3. ContentItem validates id
4. ContentItem validates title
5. ContentItem initializes parent state
6. Control returns to VideoLesson constructor
7. VideoLesson validates videoUrl
8. VideoLesson validates duration
9. VideoLesson initializes child state
10. Object creation completes

Parent Validation Failure Stops Creation

For this call, the parent constructor rejects the id because id <= 0:

java
new VideoLesson(-1L, "Parent Construction",
        "https://example.com/video",
        15);

The parent constructor throws an IllegalArgumentException, so the rest of the VideoLesson constructor never runs. No object is created, and the caller does not receive a VideoLesson.

Constructor Safety

During construction, avoid depending on child-specific behavior before child state has been initialized. The parent constructor should primarily initialize and validate its own state. Detailed overriding behavior comes in the next lesson. A useful rule:

Constructors should keep initialization predictable.

Important Terms

super(...)

A call to a parent constructor.

Automatic super()

If there is no explicit parent-constructor call, the compiler inserts a call to the no-argument parent constructor.

Parent State

Superclass-defined state that is initialized as part of the child object.

Inherited Member

A member made available to the child through the parent.

protected

A subclass-oriented access level.

final Method

A method that cannot be overridden.

final Class

A class that cannot be extended.

Predict the Result

Question 1

java
public class Parent {

    public Parent() {
        System.out.println("Parent");
    }
}
java
public class Child extends Parent {

    public Child() {
        System.out.println("Child");
    }
}

Then:

java
new Child();

What prints?

Answer

text
Parent
Child

The parent constructor executes before the child constructor body.

Question 2

Parent:

java
public Parent(int value) {
}

Child:

java
public Child() {
}

Will it compile if the parent has no no-argument constructor?

Answer

No. The compiler tries to call super();, but no matching parent constructor exists.

Question 3

java
public class Parent {

    private int value;
}

Can the child directly do value = 10;?

Answer

No. The field is private to the parent class.

Question 4

java
public final class CourseCode {

}

Can another class extend it?

Answer

No. A final class cannot be extended.

Question 5

java
public final long getId() {
    return id;
}

Can a child override getId()?

Answer

No. A final method cannot be overridden.

Common Mistakes

Adding an Empty Parent Constructor to Make the Child Compile

When a child constructor does not compile because ContentItem has no no-argument constructor, the tempting fix is to add public ContentItem() {}. That lets content exist without an id or title. The fix belongs in the child: call super(id, title) with real values (see "Do Not Weaken Parent Invariants").

Making Fields protected Just for Convenience

A protected String title; lets any subclass write title = ""; and skip the parent's validation. Keep the field private and give subclasses methods instead.

Assuming Constructors Are Inherited

If VideoLesson declares only the four-argument constructor, new VideoLesson(1L, "Intro") does not compile, even though ContentItem(long, String) exists. Each class declares the constructors it offers.

Putting a Statement Before super(...)

Logging or checking something before super(id, title) fails in Java 21 with error: call to super must be first statement in constructor. Move the statement after the call.

Practice Exercise 1: Add QuizLesson

Solutions: lesson 4.10.

Create:

java
public class QuizLesson
        extends ContentItem

with the fields questionCount and passingScore. Rules:

  • questionCount > 0
  • passingScore is between 0 and 100
  • Use super(id, title) to initialize parent state

Practice Exercise 2: Predict Constructor Order

Hierarchy:

Diagram আঁকা হচ্ছে…

Each constructor prints one line. What will the output order be when new LiveVideoLesson(...) is executed?

Practice Exercise 3: Fix the Constructor

Parent:

java
public ContentItem(long id, String title) {
}

Child:

java
public VideoLesson(String videoUrl) {
    this.videoUrl = videoUrl;
}

Fix the child constructor so that it includes the required parent state.

Practice Exercise 4: Replace a protected Field

Weak parent:

java
protected boolean published;

Refactor so that:

  • The field is private
  • The parent exposes isPublished()
  • The parent exposes controlled publish()

Practice Exercise 5: this(...) and super(...)

Create these two constructors:

java
VideoLesson(long id, String title, String videoUrl)
VideoLesson(
        long id, String title, String videoUrl, int durationInMinutes)

The shorter constructor should delegate using this(...).

Practice Exercise 6: Choose final

Decide whether final is reasonable for:

  1. CourseCode class
  2. ContentItem.getId()
  3. ContentItem.publish()
  4. VideoLesson class

Explain briefly.

Knowledge Check

Question 1

Why does the parent constructor execute when a child object is created?

Question 2

What does super(...) do?

Question 3

Where must super(...) appear?

Question 4

What does the compiler try if there is no explicit parent-constructor call?

Question 5

When does automatic super() fail?

Question 6

Are constructors inherited?

Question 7

What is the difference between this(...) and super(...)?

Question 8

Can one constructor contain two direct constructor-chaining calls?

Question 9

Can a child directly access a parent's private field?

Question 10

How can a child access parent state?

Question 11

Why can a protected field be risky?

Question 12

Why can a protected member be useful?

Question 13

What does a final method prevent?

Question 14

What does a final class prevent?

Question 15

What happens to child construction if parent validation fails?

Knowledge Check Answers

Answer 1

To initialize the child object's parent-defined state into a valid state.

Answer 2

It calls a matching constructor in the parent class.

Answer 3

As the first statement of the constructor.

Answer 4

It tries to call the no-argument super();.

Answer 5

When the parent has no accessible no-argument constructor.

Answer 6

No. The child declares its own constructors.

Answer 7

this(...) calls another constructor in the same class. super(...) calls a parent constructor.

Answer 8

No. Only one constructor-chaining call can be the first statement.

Answer 9

No.

Answer 10

By using the parent's accessible public or protected methods.

Answer 11

The child can bypass parent validation or invariants through direct state mutation.

Answer 12

When the parent intentionally wants to allow subclass access or customization.

Answer 13

Method overriding.

Answer 14

Subclass creation.

Answer 15

Object creation fails and child initialization does not complete.

Lesson Summary

  • Creating a child object also initializes its parent state: super(...) calls the parent constructor, which runs (and validates) before the rest of the child constructor.
  • super(...) must be the first statement; without it the compiler inserts super(), which fails if the parent has no no-argument constructor. Do not add an empty one that weakens the parent's rules.
  • Constructors are not inherited; this(...) chains within a class and must eventually reach super(...).
  • If parent validation fails, the child object is never created.
  • A child cannot touch the parent's private fields; it uses the parent's methods, so each class keeps owning and validating its own state.
  • protected gives subclasses deliberate access; prefer protected methods over protected fields.
  • A final method cannot be overridden and a final class cannot be extended; use them where specialization should be prohibited.

আগে ভাবুন

শীঘ্রই আসছে

Checkpoint প্রশ্ন, যেখানে lesson উত্তর দেখানোর আগে আপনাকে একটি উত্তর বেছে নিতে হবে।