Inheritance, Interfaces, and Polymorphism
Parent Construction, `super`, and Inherited Access
আপনি একটি free preview lesson দেখছেন।
Lesson Overview
When a child object is created, only the child class's state is not initialized.
Its parent portion must also be initialized into a valid state.
Consider:
VideoLesson videoLesson =
new VideoLesson(
1L,
"Inheritance Basics",
"https://cdn.liveklass.io/videos/inheritance",
18
);
A VideoLesson is a ContentItem.
Conceptually, the object's state is:
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.
Java uses:
super(...)
for this purpose.
In this lesson, we will learn:
- Why the parent constructor executes
super(...)- Automatic
super() - Constructor execution order
- Reusing parent validation
this(...)vs.super(...)- Parent
privatestate protectedaccessfinalmethodsfinalclasses
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(...)andsuper(...) - Understand access rules for parent
privatemembers - Explain the basic purpose of
protected - Understand the effects of a
finalmethod and afinalclass
Parent State Must Be Initialized
Parent:
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;
}
}
Child:
public class VideoLesson
extends ContentItem {
private final String videoUrl;
private final int durationInMinutes;
}
A VideoLesson object also contains parent-defined state.
Therefore, the parent constructor must execute.
Calling the Parent Constructor
To call the parent constructor from a child constructor, use:
super(...)
Example:
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:
ContentItem(
long id,
String title
)
Why Is super(...) Necessary?
Parent fields:
private final long id;
private final String title;
The child cannot directly assign them.
Invalid:
public VideoLesson(
long id,
String title
) {
this.id =
id;
this.title =
title;
}
because id and title are private fields of the parent.
Correct approach:
super(
id,
title
);
The parent initializes its own state.
Parent Validation Is Reused
The parent constructor already validates:
id
title
So the child constructor does not need to repeat the same validation.
public VideoLesson(
long id,
String title,
String videoUrl,
int durationInMinutes
) {
super(
id,
title
);
// validate video-specific state
}
Responsibility:
ContentItem
→ validates parent state
VideoLesson
→ validates child-specific state
This reduces duplication and centralizes the rules.
Constructor Execution Order
Suppose the hierarchy is:
Object
└── ContentItem
└── VideoLesson
When:
new VideoLesson(...);
is executed, construction proceeds from parent toward child.
Simplified:
ContentItem constructor
↓
VideoLesson constructor
Observing the Order
public class ContentItem {
public ContentItem(
long id,
String title
) {
System.out.println(
"ContentItem constructor"
);
}
}
public class VideoLesson
extends ContentItem {
public VideoLesson(
long id,
String title
) {
super(
id,
title
);
System.out.println(
"VideoLesson constructor"
);
}
}
Usage:
new VideoLesson(
1L,
"Inheritance"
);
Output:
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.
Example:
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:
public VideoLesson(
long id,
String title
) {
System.out.println(
"Creating video"
);
super(
id,
title
);
}
This will not compile.
Correct:
public VideoLesson(
long id,
String title
) {
super(
id,
title
);
System.out.println(
"Creating video"
);
}
A constructor-chaining call must be the first statement.
Automatic super()
If a child constructor does not explicitly write super(...), the compiler tries to call:
super();
Example parent:
public class ContentItem {
public ContentItem() {
}
}
Child:
public class VideoLesson
extends ContentItem {
public VideoLesson() {
}
}
Conceptually, the child constructor behaves like:
public VideoLesson() {
super();
}
When Automatic super() Fails
Parent:
public class ContentItem {
public ContentItem(
long id,
String title
) {
}
}
There is no:
ContentItem()
Child:
public class VideoLesson
extends ContentItem {
public VideoLesson() {
}
}
The compiler tries:
super();
but no matching parent constructor exists.
Result:
Compile error
The child must explicitly call a valid parent constructor:
public VideoLesson(
long id,
String title
) {
super(
id,
title
);
}
Do Not Weaken Parent Invariants
A poor fix would be:
public ContentItem() {
}
just so that the child can compile.
If the parent requires:
valid id
valid title
then 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:
public ContentItem(
long id,
String title
) {
}
That does not automatically give the child:
VideoLesson(
long id,
String title
)
The child must declare its own constructor:
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
title
A video lesson additionally requires:
videoUrl
durationInMinutes
Complete constructor:
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:
this(...)
calls another constructor in the same class.
super(...)
calls a constructor in the parent class.
Summary:
| Syntax | Calls |
|---|---|
this(...) | Same-class constructor |
super(...) | Parent-class constructor |
You Cannot Directly Use Both
A constructor can have only one first statement.
Invalid:
public VideoLesson(
long id,
String title
) {
this(
id,
title,
"https://example.com",
1
);
super(
id,
title
);
}
If a constructor uses:
this(...)
the delegated constructor must eventually call:
super(...)
Valid Constructor Chaining
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(
"Duration must be positive."
);
}
this.videoUrl =
videoUrl.strip();
this.durationInMinutes =
durationInMinutes;
}
}
Flow:
3-argument VideoLesson constructor
↓
4-argument VideoLesson constructor
↓
ContentItem constructor
Parent private State
Parent:
private final String title;
The child cannot directly write:
System.out.println(
title
);
because title is private to ContentItem.
Access Parent State Through Methods
Parent:
public String getTitle() {
return title;
}
Child:
public void printTitle() {
System.out.println(
getTitle()
);
}
This works.
Parent state remains encapsulated.
Private State Still Belongs to the Parent Portion
A VideoLesson object contains the state initialized by ContentItem.
But child code cannot directly access the parent's private fields.
Think:
Parent owns the field
↓
Parent exposes controlled behavior
↓
Child uses that behavior
Example:
videoLesson.publish();
The parent method can modify its own private:
published
field.
The child does not need direct field access.
Inherited Public Methods
If the parent declares:
public boolean isPublished() {
return published;
}
the child can use:
videoLesson.isPublished();
Public parent methods become available through child objects.
The protected Modifier
protected can give subclasses access to a member.
Example:
protected String contentTypeLabel() {
return "Content";
}
Child:
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:
title =
"";
and bypass parent validation.
This weakens encapsulation.
Prefer:
private fields
+
public/protected methods
when possible.
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
No modifier:
boolean validate() {
}
means package-private.
A subclass in another package cannot directly access it.
protected is different because it can participate in subclass access across packages.
The exact cross-package rules become more relevant in larger inheritance hierarchies.
For now:
package-private
→ package collaboration
protected
→ subclass-oriented access
final Method
A parent method marked final cannot be overridden by child classes.
public final long getId() {
return id;
}
The child cannot redefine:
@Override
public long getId() {
return 999L;
}
Compile error.
Why Use a final Method?
Use final when a specific inherited behavior must remain unchanged.
Example:
public final long getId() {
return id;
}
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.
public final class CourseCode {
}
Invalid:
public class SpecialCourseCode
extends CourseCode {
}
Why Make a Class final?
It can be useful when:
- Inheritance does not make sense
- Immutable value behavior should stay fixed
- Subclass specialization is undesirable
Our earlier:
CourseCode
is a good example.
Complete Example
ContentItem.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
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
public class Main {
public static void main(
String[] args
) {
VideoLesson videoLesson =
new VideoLesson(
1L,
"Parent Construction",
"https://cdn.liveklass.io/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:
1
Parent Construction
true
https://cdn.liveklass.io/videos/super
Construction Flow
For:
new VideoLesson(
1L,
"Parent Construction",
"https://cdn.liveklass.io/videos/super",
15
);
High-level flow:
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
If:
new VideoLesson(
-1L,
"Parent Construction",
"https://example.com/video",
15
);
the parent constructor rejects:
id <= 0
The child initialization does not complete.
The caller does not receive a valid VideoLesson object.
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.
Useful rule:
Constructors should keep initialization predictable.
Common Mistakes
Forgetting the Parent Constructor Requirement
Parent:
public ContentItem(
long id,
String title
) {
}
Child:
public VideoLesson() {
}
will not compile unless a matching parent constructor is called.
Weak No-Argument Parent Constructor
Do not add:
public ContentItem() {
}
just to avoid passing required parent state.
super(...) After Another Statement
Invalid.
It must be the first constructor statement.
Accessing a Parent private Field Directly
Invalid:
System.out.println(
title
);
if title privately belongs to the parent.
Making Fields protected Just for Convenience
This can expose parent invariants to uncontrolled child mutation.
Assuming Constructors Are Inherited
They are not.
Calling Both this(...) and super(...) Directly
Both cannot appear as separate constructor-chain calls in the same constructor.
Making Every Method final
Use final only when specialization should intentionally be prohibited.
Important Terms
super(...)
A call to a parent constructor.
Automatic super()
If there is no explicit parent-constructor call, the compiler attempts to call 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.
Practice Exercise 1: Add QuizLesson
Create:
public class QuizLesson
extends ContentItem
Fields:
questionCount
passingScore
Rules:
questionCount > 0passingScoreis between0and100- Use
super(id, title)to initialize parent state
Practice Exercise 2: Predict Constructor Order
Hierarchy:
ContentItem
↓
VideoLesson
↓
LiveVideoLesson
Each constructor prints one line.
What will the output order be when:
new LiveVideoLesson(...)
is executed?
Practice Exercise 3: Fix the Constructor
Parent:
public ContentItem(
long id,
String title
) {
}
Child:
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:
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:
VideoLesson(
long id,
String title,
String videoUrl
)
and:
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:
CourseCodeclassContentItem.getId()ContentItem.publish()VideoLessonclass
Explain briefly.
Predict the Result
Question 1
public class Parent {
public Parent() {
System.out.println(
"Parent"
);
}
}
public class Child
extends Parent {
public Child() {
System.out.println(
"Child"
);
}
}
Then:
new Child();
What prints?
Question 2
Parent:
public Parent(
int value
) {
}
Child:
public Child() {
}
Will it compile if the parent has no no-argument constructor?
Question 3
public class Parent {
private int value;
}
Can the child directly do:
value =
10;
Question 4
public final class CourseCode {
}
Can another class extend it?
Question 5
public final long getId() {
return id;
}
Can a child override getId()?
Predict the Result Answers
Answer 1
Parent
Child
The parent constructor executes before the child constructor body.
Answer 2
No.
The compiler tries to call:
super();
but no matching parent constructor exists.
Answer 3
No.
The field is private to the parent class.
Answer 4
No.
A final class cannot be extended.
Answer 5
No.
A final method cannot be overridden.
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
In this lesson, we learned:
- Parent state is also initialized when a child object is created
super(...)calls a parent constructor- Child classes can reuse parent validation
- Parent initialization completes before child initialization
super(...)must be the first constructor statement- If there is no explicit call, the compiler attempts to call
super() - If the parent has no no-argument constructor, a matching
super(...)must be called explicitly - An empty constructor should not be added if it weakens parent invariants
- Constructors are not inherited
- A child can initialize its own additional required state
this(...)performs same-class constructor chainingsuper(...)calls a parent constructor- A child cannot directly access parent
privatestate - A child can use encapsulated parent state through parent methods
protectedprovides subclass-oriented accessprotectedfields can weaken parent encapsulation- A
finalmethod prevents overriding - A
finalclass prevents inheritance - If parent validation fails, child construction does not complete
- Parent and child each own validation responsibility for their own state