DEV Community

Md Jamilur Rahman
Md Jamilur Rahman

Posted on

Inheritance in Java: How Child Classes Borrow From Parents

Most programming tutorials explain inheritance with animals. A Dog is a Mammal. A Mammal is an Animal. It works, but it does not match what you actually build at work. You will not write a GoldenRetriever class anytime soon.

Here is what you will actually use inheritance for. You want to write a piece of logic once and reuse it in multiple classes. You want a general "base" class that handles the boring work, and specific "child" classes that only add what makes them different.

That is inheritance in practice. One class extends another and inherits its fields and methods.

The Parent and Child Idea

Imagine you run a small e-commerce site in Bangladesh. You sell products from multiple categories - electronics, clothing, groceries. Every product has a name, a price, and a seller. But an electronic item has a warranty period, while a grocery item has an expiry date.

Without inheritance, you would write the name, price, and seller fields in every category class. You would repeat the same getters and setters three times. That is wasteful and hard to maintain. When you add a new field like "discount percentage," you have to touch every class.

With inheritance, you put the shared fields in one place and let the specific classes inherit them.

The official Java documentation defines inheritance as: "a mechanism wherein a new class is derived from an existing class." The existing class is called the superclass (parent). The new class is called the subclass (child).

Writing the Parent Class

Here is a simple parent class that holds what every product shares.

public class Product {
    String name;
    double price;
    String seller;

    public Product(String name, double price, String seller) {
        this.name = name;
        this.price = price;
        this.seller = seller;
    }

    public void displayInfo() {
        System.out.println(name + " - Tk " + price + " by " + seller);
    }
}
Enter fullscreen mode Exit fullscreen mode

This is a normal class. Nothing special. The "parent" label comes from how other classes use it.

Writing a Child Class

Now let us create a child class that extends Product.

public class Electronics extends Product {
    int warrantyMonths;

    public Electronics(String name, double price, String seller, int warrantyMonths) {
        super(name, price, seller);
        this.warrantyMonths = warrantyMonths;
    }

    public void printWarranty() {
        System.out.println(name + " has " + warrantyMonths + " months warranty.");
    }
}
Enter fullscreen mode Exit fullscreen mode

Three things to notice here.

First, the extends keyword. This tells Java that Electronics is a child of Product. It inherits all the fields and methods from Product.

Second, the super(...) call in the constructor. This is mandatory. Java requires the child constructor to call the parent constructor first, before adding its own fields. If you leave it out, the compiler complains. The super call sets up the inherited fields name, price, and seller using the parent's constructor logic.

Third, name is accessible directly in printWarranty(). Even though it was declared in Product, the child inherited it. You can use it like it belongs to the child. Because it does.

The Java Language Specification explains that a subclass inherits all accessible members of its superclass. Private members are inherited but not directly accessible by name. We will get to that.

Method Overriding: When the Child Wants Its Own Version

Sometimes the parent's method is too generic. The displayInfo in Product prints name, price, seller. But for electronics, you might want to also show the warranty. For groceries, you might want to show the expiry date.

Inheritance lets you override a parent method. You write the same method signature in the child class with different logic. Java calls the child's version when the object is actually a child instance.

public class Electronics extends Product {
    int warrantyMonths;

    public Electronics(String name, double price, String seller, int warrantyMonths) {
        super(name, price, seller);
        this.warrantyMonths = warrantyMonths;
    }

    @Override
    public void displayInfo() {
        super.displayInfo();
        System.out.println("Warranty: " + warrantyMonths + " months");
    }
}
Enter fullscreen mode Exit fullscreen mode

The @Override annotation tells Java you intend to override a method from the parent. It is not required, but you should always use it. If you misspell the method name, the compiler catches the mistake immediately because it checks the parent class for a matching signature.

The super.displayInfo() call inside the override is a deliberate choice. You can either call the parent version and add to it, or completely replace it. In this example we call the parent first, then add the warranty line. The output becomes:

Galaxy S25 - Tk 120000 by Samsung
Warranty: 24 months
Enter fullscreen mode Exit fullscreen mode

Baeldung has a comprehensive guide on overriding rules at: Method Overriding in Java

What Gets Inherited and What Does Not

Beginners often assume everything passes from parent to child. That is not true.

  • public and protected fields and methods: fully inherited.
  • package-private (default) members: inherited only if the child is in the same package.
  • private members: inherited but not accessible by name. They exist in the object (the memory is allocated), but you cannot write privateField directly in the child class. You need a public or protected getter from the parent.
  • constructors: NOT inherited. The child must define its own constructor and call super(...).
  • static methods: technically inherited but you should call them using the parent class name, not through an object instance.

This distinction matters in real projects. Spring's @Service classes often extend a base service that has private logger fields and protected utility methods. The child classes access the utilities but do not touch the logger directly.

The protected Keyword: Just Right

If you want child classes to access a field directly but keep it hidden from the outside world, use protected.

public class Product {
    protected String name;
    private double price;       // private - children can't see this directly
    protected String seller;
}
Enter fullscreen mode Exit fullscreen mode

Now Electronics can read name and seller directly but must use a getter for price. This gives you fine control over what children can see versus what the public can see.

The Oracle tutorials cover access control in detail, including the full table of what each access modifier allows.

A Real Example: Base Repository Pattern

Here is where you actually see inheritance in production code. Many Java projects use a base repository class.

public class BaseRepository<T> {
    protected Connection connection;

    public BaseRepository(Connection connection) {
        this.connection = connection;
    }

    protected T findById(long id, String table, RowMapper<T> mapper) {
        // common database query logic here
        return null;
    }
}

public class UserRepository extends BaseRepository<User> {
    public UserRepository(Connection connection) {
        super(connection);
    }

    public User findByEmail(String email) {
        // uses inherited connection field
        // calls inherited findById or adds custom query
        return null;
    }
}
Enter fullscreen mode Exit fullscreen mode

The UserRepository gets the database connection setup and the common findById method for free. It only adds what is specific to users. You write the boring JDBC boilerplate once in BaseRepository and never again.

What Java Does NOT Allow

Java has single inheritance for classes. A class can extend only one parent.

public class Laptop extends Electronics, Product {  // will not compile
}
Enter fullscreen mode Exit fullscreen mode

This does not compile. Java deliberately avoids the complexity of multiple inheritance that C++ has. If you need behavior from multiple sources, use interfaces. We covered interfaces in the previous article. The combination of single inheritance for "is-a" relationships and multiple interfaces for "can-do" capabilities is one of Java's most pragmatic design decisions.

Common Beginner Trap: Using Inheritance for Code Reuse Only

The temptation is to use inheritance just to share code. You see two classes with similar methods and think "let me make one extend the other."

Inheritance creates an "is-a" relationship. A Dog is a Mammal. An Electronics is a Product. If the relationship is not truly "is-a," use composition instead. Give the class a field of the other type rather than extending it.

The design principle is called "favor composition over inheritance." Joshua Bloch wrote about it in Effective Java, and Baeldung explains it well. The short version: if you are extending a class just to borrow two methods, you are probably doing it wrong. Use a helper class or a utility method instead.

Quick Summary

  • A child class uses extends to inherit from a parent class.
  • The child calls super(...) in its constructor to initialize parent fields.
  • The @Override annotation marks methods you are replacing from the parent.
  • protected fields are visible to child classes but not to the public.
  • Private members and constructors are not directly accessible in child classes.
  • Java allows only single class inheritance. Use interfaces for multiple capabilities.
  • Use inheritance for "is-a" relationships. Use composition for "has-a" relationships.

Based on dev.java/learn: Inheritance

Top comments (0)