Lesson 51 / 140
লিখিত lesson · 15 মিনিট

Introduction to Inheritance

Learn when inheritance fits, create a hierarchy with `extends`, add specialized state, and tell compile-time from runtime types.

Preview

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

Lesson Overview

As an application grows, we may find classes that share common state and behavior. Suppose Tarkev has three types of lesson content: VideoLesson, ArticleLesson and QuizLesson. Each has common information (ID, title, published status), and at the same time each has specialized information:

text
VideoLesson
→ video URL
→ duration

ArticleLesson
→ article content
→ reading time

QuizLesson
→ question count
→ passing score

We do not want to write that common part three times. But removing duplication is not enough reason on its own: inheritance is useful when the classes genuinely share a meaningful common type (a VideoLesson is a ContentItem). This lesson shows how to build such a hierarchy with extends, what a parent-type reference can and cannot do, and when composition is the better choice.

Learning Objectives

After completing this lesson, you will be able to:

  • Explain the purpose of inheritance
  • Identify parent and child classes
  • Create a simple hierarchy using extends
  • Use inherited methods
  • Add specialized state to a child class
  • Identify a valid is-a relationship
  • Assign a child object to a parent-type reference
  • Distinguish compile-time and runtime types
  • Understand the difference between code reuse and type relationships
  • Identify incorrect inheritance designs

The Duplication Problem

Without inheritance, VideoLesson holds its own copy of the common state and behavior:

java
public class VideoLesson {

    private final long id;
    private final String title;

    private boolean published;

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

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

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

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

        published = true;
        return true;
    }

    public String getTitle() {
        return title;
    }
}

If ArticleLesson also needs the same id, title, published, validation, publish() and getTitle(), we would need to write them again, creating duplication.

But duplication alone does not mean inheritance should be used. First ask:

Are VideoLesson and ArticleLesson genuinely specialized forms of the same broader type?

Here, the answer is yes. Both are learning content, so a common parent can be meaningful.

What Is Inheritance?

Inheritance allows one class to inherit the accessible behavior and type contract of another class and create a specialized type. In Java, class inheritance is declared using the extends keyword:

java
public class ContentItem {

}
java
public class VideoLesson extends ContentItem {

}

Here, ContentItem is the parent (superclass) and VideoLesson is the child (subclass). The declaration means "VideoLesson is a ContentItem".

Creating the Parent Class

Common state and behavior can be placed in the parent:

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 boolean publish() {
        if (published) {
            return false;
        }

        published = true;
        return true;
    }

    public long getId() {
        return id;
    }

    public String getTitle() {
        return title;
    }

    public boolean isPublished() {
        return published;
    }
}

ContentItem owns the common content behavior.

Creating a Child Class

java
public class VideoLesson extends ContentItem {

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

super(...) calls the parent constructor. We will learn the details in the next lesson. For now, simply observe:

Even when a child object is created, the parent's required state must still be initialized.

Inherited Behavior

VideoLesson did not declare publish(), getId(), getTitle() or isPublished() in its own class. Even so, this works:

java
VideoLesson lesson =
        new VideoLesson(1L, "Introduction to Inheritance");
lesson.publish();
System.out.println(lesson.getTitle());
System.out.println(lesson.isPublished());

Output:

text
Introduction to Inheritance
true

The methods are inherited from ContentItem.

Child-Specific State

A child can inherit common behavior while also keeping its own specialized state:

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

        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;
    }
}

Now VideoLesson has:

text
Inherited
→ getId()
→ getTitle()
→ publish()
→ isPublished()

Own
→ getVideoUrl()
→ getDurationInMinutes()

Another Child Type

java
public class ArticleLesson extends ContentItem {

    private final String content;
    private final int estimatedReadingMinutes;

    public ArticleLesson(
            long id, String title, String content, int estimatedReadingMinutes) {
        super(id, title);

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

        if (estimatedReadingMinutes <= 0) {
            throw new IllegalArgumentException("Reading time must be positive.");
        }

        this.content = content.strip();
        this.estimatedReadingMinutes = estimatedReadingMinutes;
    }

    public String getContent() {
        return content;
    }

    public int getEstimatedReadingMinutes() {
        return estimatedReadingMinutes;
    }
}

Type Hierarchy

Now we have:

Diagram আঁকা হচ্ছে…

Later:

Diagram আঁকা হচ্ছে…

The parent is the common type, and the children are specialized types.

is-a and has-a

The is-a Relationship

The most useful question when evaluating inheritance is:

Is the child genuinely a type of the parent?

Valid:

text
VideoLesson is a ContentItem
ArticleLesson is a ContentItem
QuizLesson is a ContentItem

Invalid:

text
Enrollment is a Course
Course is a Learner
Course is a Lesson

Invalid relationships should not be modeled with inheritance.

has-a Is Different

"Enrollment has a Course" and "Course has Lessons" are has-a relationships. We already learned composition for them:

java
public class Enrollment {

    private final Course course;
}

Compare:

text
VideoLesson is a ContentItem
→ inheritance

Enrollment has a Course
→ composition

Parent Reference Can Hold a Child Object

Java allows:

java
ContentItem content = new VideoLesson(1L, "Java Inheritance",
                "https://cdn.tarkev.com/video/1",
                15);

Here, the reference type is ContentItem and the actual object is a VideoLesson. This is allowed because every VideoLesson is a ContentItem, so wherever a ContentItem is expected, a VideoLesson can be supplied.

That is why one method can handle multiple child types:

java
public static void printTitle(ContentItem content) {
    System.out.println(content.getTitle());
}
java
printTitle(videoLesson);
printTitle(articleLesson);

Both calls work because the caller depends only on the common parent contract, not on the specific child type.

Substitutability — The Core Idea

An important design idea of inheritance is:

Wherever a parent type is expected, a child object should be usable there meaningfully.

For example:

java
public static boolean publish(ContentItem content) {
    return content.publish();
}

Both calls are valid, because both are publishable ContentItem objects:

java
publish(videoLesson);
publish(articleLesson);

Compile-Time Type vs. Runtime Type

Look again at ContentItem content = new VideoLesson(...) from the previous section. There are two types to notice:

text
Compile-time type
→ ContentItem

Runtime type
→ VideoLesson

Compile-Time Type Controls Available Members

This works, because those methods are part of the ContentItem contract:

java
content.getTitle();
content.publish();

But this will not compile:

java
content.getVideoUrl();

javac reports:

text
error: cannot find symbol
  symbol:   method getVideoUrl()
  location: variable content of type ContentItem

Why? getVideoUrl() is only part of the VideoLesson contract. The reference variable's compile-time type is ContentItem, and that type does not know about this method.

Child Reference Exposes Child-Specific API

If we declare the variable as VideoLesson videoLesson = new VideoLesson(...) instead, both videoLesson.getTitle() and videoLesson.getVideoUrl() are available. The child exposes the parent API plus its child-specific API.

Upcasting

Assigning a child object or reference to a parent type is commonly called upcasting:

java
VideoLesson videoLesson = new VideoLesson(1L, "Inheritance",
                "https://example.com/video",
                10);
ContentItem content = videoLesson;

No explicit cast is required. The conversion is naturally safe because every VideoLesson is a ContentItem.

Inheritance Is Not Just Code Reuse

Suppose:

java
public class Course extends ValidationUtils {

}

Maybe Course can reuse some validation methods. But ask: is a Course a ValidationUtils? No. So the inheritance relationship is meaningless. Code reuse alone is not a sufficient reason for inheritance.

Composition May Be Better

If a class uses another capability, composition usually matches the relationship better:

text
Course has a pricing policy
Course has lessons
Enrollment has a learner

For example:

java
public class Course {

    private PricingPolicy pricingPolicy;
}

instead of:

java
public class Course extends PricingPolicy {
}

Java Supports Single Class Inheritance

A Java class can directly extend only one class. VideoLesson extends ContentItem is fine, but Java does not support multiple parent classes, so this declaration does not compile:

java
public class VideoLesson extends ContentItem, MediaResource

javac stops at the comma with error: '{' expected. We will learn about interfaces later.

Every Class Ultimately Extends Object

If a class has no explicit parent:

java
public class Learner {

}

Java makes it extend java.lang.Object automatically. Every class is part of the Object hierarchy. Familiar methods such as equals(), hashCode(), toString() and getClass() come from Object; we used some of them in the previous module.

Keep Hierarchies Simple

Technically, a hierarchy can be deeper:

Diagram আঁকা হচ্ছে…

But unnecessary levels increase complexity. Avoid designing:

Diagram আঁকা হচ্ছে…

unless every level has a clear meaning. A useful rule:

Start with the smallest meaningful hierarchy.

When Inheritance Is Appropriate

Consider inheritance when:

  • A valid is-a relationship exists. For example, VideoLesson is a ContentItem.
  • The parent concept is meaningful. ContentItem is a real abstraction in the domain.
  • The child can correctly use the parent contract. If every ContentItem can publish, child types should meaningfully support that behavior.
  • The child adds specialization. For example, VideoLesson adds a video URL and a duration.

When Inheritance Is Probably Wrong

Inheritance is probably wrong when:

  • The relationship is has-a. Enrollment has a Course: use composition.
  • The only reason is code reuse. Course extends StringHelper is the wrong abstraction.
  • The child does not support parent behavior. If the parent offers publish() but a child must always reject or disable that operation, the hierarchy may be wrong.
  • The hierarchy exists only for configuration differences. FreeCourse, PaidCourse and DiscountedCourse may not need three subclasses. A Course with price-related state or a collaborator may be simpler.

Complete Example

We use ContentItem from "Creating the Parent Class", VideoLesson from "Child-Specific State" and ArticleLesson from "Another Child Type", unchanged. Only Main is new.

Main.java

java
public class Main {

    public static void main(String[] args) {
        VideoLesson videoLesson =
                new VideoLesson(1L, "Introduction to Inheritance",
                        "https://cdn.tarkev.com/videos/inheritance",
                        18);

        ArticleLesson articleLesson =
                new ArticleLesson(
                        2L,
                        "Understanding Type Hierarchies",
                        "A hierarchy models related types.", 7);

        videoLesson.publish();
        articleLesson.publish();

        printSummary(videoLesson);
        printSummary(articleLesson);
    }

    private static void printSummary(ContentItem content) {
        System.out.println("ID: " + content.getId());
        System.out.println("Title: " + content.getTitle());
        System.out.println("Published: " + content.isPublished());
        System.out.println();
    }
}

Output:

text
ID: 1
Title: Introduction to Inheritance
Published: true

ID: 2
Title: Understanding Type Hierarchies
Published: true

One method handled different specialized objects through their common parent type.

Important Terms

Inheritance

A mechanism where one class inherits another class's type contract and accessible behavior to create a specialized type.

Parent Class / Superclass

The class being extended.

Child Class / Subclass

The class that extends the parent.

extends

The keyword used to declare class inheritance.

Type Hierarchy

An organized relationship between parent and child types.

is-a

The child is a specialized type of the parent.

Upcasting

Assigning a child object to a parent-type reference.

Compile-Time Type

The declared type of a reference variable.

Runtime Type

The concrete type of the object that was actually created.

Predict the Result

Question 1

java
VideoLesson video = new VideoLesson(1L, "Inheritance",
                "https://example.com/video",
                10);
System.out.println(video.getTitle());

Will it compile?

Answer

Yes. getTitle() is inherited from the parent ContentItem.

Question 2

java
ContentItem content = new VideoLesson(1L, "Inheritance",
                "https://example.com/video",
                10);
System.out.println(content.getVideoUrl());

Will it compile?

Answer

No. The reference's compile-time type is ContentItem, and getVideoUrl() is not part of the parent contract.

Question 3

java
ContentItem first = new ArticleLesson(1L, "Objects", "Content", 5);
ContentItem second = first;
first.publish();
System.out.println(second.isPublished());

What is the output?

Answer

text
true

first and second refer to the same object.

Common Mistakes

Using Inheritance As Soon As You See Duplicate Code

Duplication can be an initial signal, but check the type relationship first: two classes that both validate a title are not necessarily the same kind of thing.

Treating has-a as is-a

Wrong:

java
Enrollment extends Course

Correct:

text
Enrollment has a Course

Calling a Child-Specific Method Through a Parent Reference

content.getVideoUrl() does not compile when content is declared as ContentItem, even if the object is a VideoLesson (see "Compile-Time Type Controls Available Members"). Declare the variable with the child type when you need the child's API.

Using Inheritance Only for Utility Reuse

Course extends ValidationUtils gives Course the helper methods, but also makes every Course "a ValidationUtils" that can be passed wherever one is expected. Use composition or call the helper instead.

Making Parent Fields protected

Exposing parent internals for child convenience can weaken encapsulation. We will study inherited access in more detail in the next lesson.

Practice Exercise 1: Create QuizLesson

Solutions: lesson 4.10.

Create:

java
public class QuizLesson
        extends ContentItem

with the additional fields questionCount and passingScore. Rules:

  • questionCount > 0
  • passingScore is between 0 and 100
  • Parent state must be initialized using super(...)

Practice Exercise 2: is-a or has-a?

Classify each pair as is-a or has-a:

  1. VideoLesson and ContentItem
  2. Course and Lesson
  3. Instructor and User
  4. Enrollment and Course
  5. QuizLesson and ContentItem

Practice Exercise 3: Parent Reference

java
ContentItem content =
        new ArticleLesson(1L, "Objects", "Article content", 5);

Which of these compile? Explain.

java
content.getTitle();
content.publish();
content.getContent();

Practice Exercise 4: Common Behavior

Write:

java
static void publishAndPrint(ContentItem content)

It should:

  • Publish the content
  • Print the title
  • Print publication status

Call it with a VideoLesson, an ArticleLesson and a QuizLesson.

Practice Exercise 5: Inheritance or Composition?

Choose inheritance or composition for each pair, and explain each using is-a or has-a:

  1. Course + Lesson
  2. VideoLesson + ContentItem
  3. Enrollment + Learner
  4. Instructor + User

Knowledge Check

Question 1

What is inheritance?

Question 2

What are parent and child classes?

Question 3

What does extends do?

Question 4

What is an inherited method?

Question 5

What is an is-a relationship?

Question 6

Can a parent-type reference hold a child object?

Question 7

Does a parent reference automatically expose child-specific methods?

Question 8

What is compile-time type?

Question 9

What is runtime type?

Question 10

Is the main purpose of inheritance only code reuse?

Question 11

What is generally better for modeling a has-a relationship?

Question 12

How many parent classes can a Java class directly extend?

Question 13

Which class hierarchy is every Java class ultimately part of?

Question 14

Why is avoiding deep hierarchies useful?

Knowledge Check Answers

Answer 1

A mechanism where one class inherits another class's type contract and accessible behavior to create a specialized type.

Answer 2

The parent defines the common type. The child extends the parent and creates a specialized type.

Answer 3

It declares one class as a subclass of another class.

Answer 4

An accessible method declared in the parent class that can be used by a child object.

Answer 5

The child is genuinely a specialized type of the parent.

Answer 6

Yes.

java
ContentItem content = new VideoLesson(...);

Answer 7

No. Available methods depend on the contract of the compile-time reference type.

Answer 8

The declared type of the reference variable.

Answer 9

The concrete type of the object that was actually created.

Answer 10

No. A meaningful common type and substitutability are more important.

Answer 11

Composition.

Answer 12

One.

Answer 13

Object.

Answer 14

As inheritance levels increase, reasoning about behavior, initialization, and dependencies becomes harder.

Lesson Summary

  • Inheritance (extends) creates a hierarchy of related types: the parent (superclass) holds common state and behavior, and each child (subclass) inherits it and adds its own.
  • Use inheritance only for a genuine is-a relationship (VideoLesson is a ContentItem); has-a relationships (Enrollment has a Course) use composition.
  • Code reuse alone is not a reason to inherit: a child must meaningfully satisfy the parent's contract wherever the parent is expected.
  • A parent-type reference can hold a child object (upcasting, no cast needed).
  • The compile-time type decides which methods you can call; the runtime type is the object actually created.
  • A Java class extends exactly one class, and every class is ultimately part of the Object hierarchy.
  • Keep hierarchies small: every level must have a clear meaning.

আগে ভাবুন

শীঘ্রই আসছে

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