Java Inheritance Tutorial

Avatar

By squashlabs, Last Updated: July 29, 2023

Java Inheritance Tutorial

Introduction to Inheritance

In object-oriented programming, inheritance is a fundamental concept that allows classes to inherit properties and methods from other classes. This enables code reuse, promotes modularity, and enhances the overall organization of code. In Java, inheritance is implemented using the “extends” keyword.

Related Article: How To Parse JSON In Java

Code Snippet: Creating a Superclass

public class Animal {
    protected String name;
    
    public Animal(String name) {
        this.name = name;
    }
    
    public void eat() {
        System.out.println(name + " is eating.");
    }
}

Code Snippet: Creating a Subclass

public class Dog extends Animal {
    public Dog(String name) {
        super(name);
    }
    
    public void bark() {
        System.out.println(name + " is barking.");
    }
}

In the example above, the class “Animal” serves as the superclass, while the class “Dog” is the subclass. The subclass inherits the “name” property and the “eat()” method from the superclass. Additionally, the subclass introduces its own unique method called “bark()”.

Defining Inheritance

Inheritance is a mechanism that allows a class to acquire the properties and methods of another class. The class that is being inherited from is called the superclass or base class, while the class that inherits is called the subclass or derived class.

Related Article: How To Convert Array To List In Java

Code Snippet: Overriding Methods

public class Cat extends Animal {
    public Cat(String name) {
        super(name);
    }
    
    @Override
    public void eat() {
        System.out.println(name + " is eating quietly.");
    }
}

In the example above, the subclass “Cat” overrides the “eat()” method inherited from the superclass “Animal”. By using the “@Override” annotation, we explicitly indicate that we are intentionally overriding the method. This allows us to provide a different implementation for the method in the subclass.

Types of Inheritance

Java supports several types of inheritance, including single inheritance, multiple inheritance (through interfaces), and multilevel inheritance.

Code Snippet: Abstract Classes and Inheritance

public abstract class Shape {
    protected int width;
    protected int height;
    
    public Shape(int width, int height) {
        this.width = width;
        this.height = height;
    }
    
    public abstract int calculateArea();
}

public class Rectangle extends Shape {
    public Rectangle(int width, int height) {
        super(width, height);
    }
    
    @Override
    public int calculateArea() {
        return width * height;
    }
}

In the example above, the class “Shape” is an abstract class that provides a blueprint for calculating the area of different shapes. The subclass “Rectangle” extends the “Shape” class and provides a concrete implementation of the “calculateArea()” method.

Related Article: How To Iterate Over Entries In A Java Map

The Syntax of Inheritance

In Java, the syntax for inheritance is straightforward. To create a subclass that inherits from a superclass, we use the “extends” keyword followed by the name of the superclass.

Code Snippet: Utilizing Super

public class Square extends Rectangle {
    public Square(int sideLength) {
        super(sideLength, sideLength);
    }
    
    @Override
    public int calculateArea() {
        return super.calculateArea();
    }
}

In the example above, the class “Square” extends the class “Rectangle”. By using the “super” keyword, we can access the superclass’s methods or properties. In this case, we use “super.calculateArea()” to calculate the area based on the superclass’s implementation.

Use Case: Inheritance for Code Reuse

One of the primary benefits of inheritance is code reuse. By defining common properties and methods in a superclass, we can reuse them in multiple subclasses without duplicating code.

Related Article: How To Split A String In Java

Code Snippet: Inheritance in GUI Components

import javax.swing.*;

public class CustomButton extends JButton {
    public CustomButton(String text) {
        super(text);
        // Additional customization for the button
    }
}

In the example above, we create a custom button class called “CustomButton” that extends the “JButton” class provided by the Java Swing library. By extending the existing button class, we inherit all the functionality and behavior of a regular button while being able to add our own customization.

Best Practice: Correct Use of Super

When using inheritance, it is essential to understand how and when to use the “super” keyword. The “super” keyword allows us to access the superclass’s methods, constructors, and properties.

Code Snippet: Creating a Subclass Constructor

public class Car {
    protected String make;
    
    public Car(String make) {
        this.make = make;
    }
}

public class SportsCar extends Car {
    protected int topSpeed;
    
    public SportsCar(String make, int topSpeed) {
        super(make);
        this.topSpeed = topSpeed;
    }
}

In the example above, the subclass “SportsCar” calls the superclass’s constructor using “super(make)”. This ensures that the “make” property from the superclass is properly initialized before setting the subclass-specific “topSpeed” property.

Related Article: How To Convert Java Objects To JSON With Jackson

Best Practice: Avoiding Inheritance Pitfalls

While inheritance is a powerful tool, it can also lead to potential pitfalls if not used carefully. Here are some best practices to avoid common inheritance issues:

– Favor composition over inheritance when appropriate.
– Avoid deep inheritance hierarchies to prevent complex dependencies.
– Design classes with a clear and cohesive purpose to ensure meaningful inheritance relationships.

Real World Example: Inheritance in Event Handling

In graphical user interfaces, event handling is a common use case where inheritance is applied. Different GUI components, such as buttons or text fields, inherit from a common superclass that handles basic event functionality.

Code Snippet: Event Handling with Inheritance

import java.awt.event.*;

public class CustomButton extends JButton implements ActionListener {
    public CustomButton(String text) {
        super(text);
        addActionListener(this);
    }
    
    @Override
    public void actionPerformed(ActionEvent e) {
        // Handle button click event
    }
}

In the example above, the class “CustomButton” extends the “JButton” class and implements the “ActionListener” interface. By implementing the “ActionListener” interface, the class can handle button click events by overriding the “actionPerformed()” method.

Related Article: Storing Contact Information in Java Data Structures

Real World Example: Inheritance in Design Patterns

In software design patterns, inheritance is frequently used to implement various patterns such as the Factory Method, Template Method, and Strategy patterns.

Code Snippet: Template Method Pattern

public abstract class Shape {
    public final void draw() {
        // Perform common drawing operations
        
        specificDraw();
        
        // Perform additional drawing operations
    }
    
    protected abstract void specificDraw();
}

public class Circle extends Shape {
    @Override
    protected void specificDraw() {
        // Draw a circle
    }
}

public class Rectangle extends Shape {
    @Override
    protected void specificDraw() {
        // Draw a rectangle
    }
}

In the example above, the abstract class “Shape” implements the template method pattern. The “draw()” method provides a common structure for drawing different shapes, while the “specificDraw()” method is implemented differently in each subclass.

Performance Consideration: Memory Usage

Inheritance can have an impact on memory usage, especially when dealing with large hierarchies or unnecessary inheritance relationships. Each subclass created consumes memory to store its own data and inherited data.

Related Article: How to Convert JSON String to Java Object

Performance Consideration: Impact on Execution Time

Inheritance can also affect execution time to some extent. Method calls in subclasses may involve traversing the inheritance hierarchy, which introduces a slight overhead compared to direct method calls.

Advanced Technique: Inheritance vs Composition

While inheritance is a powerful mechanism, it is not always the best solution. In some cases, composition, where objects are composed of other objects, may offer more flexibility and maintainability.

Code Snippet: Composition Example

public class Car {
    private Engine engine;
    
    public Car(Engine engine) {
        this.engine = engine;
    }
    
    public void start() {
        engine.start();
    }
}

public interface Engine {
    void start();
}

public class GasolineEngine implements Engine {
    @Override
    public void start() {
        // Start the engine using gasoline
    }
}

public class ElectricEngine implements Engine {
    @Override
    public void start() {
        // Start the engine using electricity
    }
}

In the example above, the class “Car” is composed of an “Engine” object. By using composition, we can easily switch between different engine types (e.g., gasoline or electric) without the need for complex inheritance relationships.

Related Article: How to Retrieve Current Date and Time in Java

Advanced Technique: Multiple Inheritance Solutions

Java does not support multiple inheritance of classes, meaning a class cannot directly inherit from multiple classes. However, multiple inheritance-like behavior can be achieved through the use of interfaces.

Code Snippet: Multiple Inheritance-like Behavior with Interfaces

public interface Flyable {
    void fly();
}

public interface Swimmable {
    void swim();
}

public class Bird implements Flyable {
    @Override
    public void fly() {
        // Fly like a bird
    }
}

public class Duck implements Flyable, Swimmable {
    @Override
    public void fly() {
        // Fly like a duck
    }
    
    @Override
    public void swim() {
        // Swim like a duck
    }
}

In the example above, the interfaces “Flyable” and “Swimmable” define common behaviors. The class “Bird” implements the “Flyable” interface, while the class “Duck” implements both the “Flyable” and “Swimmable” interfaces, demonstrating a form of multiple inheritance.

Error Handling: Common Inheritance Mistakes

When working with inheritance, it is crucial to be aware of common mistakes that can lead to code issues or unexpected behavior.

Related Article: How to Reverse a String in Java

Error Handling: Debugging Inheritance Issues

Debugging inheritance-related problems can be challenging, but by following best practices and utilizing debugging tools, it is possible to identify and resolve issues efficiently.

How to Generate Random Integers in a Range in Java

Generating random integers within a specific range in Java is made easy with the Random class. This article explores the usage of java.util.Random and ThreadLocalRandom... read more

Java Equals Hashcode Tutorial

Learn how to implement equals and hashcode methods in Java. This tutorial covers the basics of hashcode, constructing the equals method, practical use cases, best... read more

How To Convert String To Int In Java

How to convert a string to an int in Java? This article provides clear instructions using two approaches: Integer.parseInt() and Integer.valueOf(). Learn the process and... read more

Java Composition Tutorial

This tutorial: Learn how to use composition in Java with an example. This tutorial covers the basics of composition, its advantages over inheritance, and best practices... read more

Java Hashmap Tutorial

This article provides a comprehensive guide on efficiently using Java Hashmap. It covers various use cases, best practices, real-world examples, performance... read more

Popular Data Structures Asked in Java Interviews

Tackle Java interview questions on popular data structures with this in-depth explanation. Learn about arrays, linked lists, stacks, queues, binary trees, hash tables,... read more