Progress8 of 14 topics

57% complete

Inheritance in Java

Inheritance is one of the four fundamental principles of object-oriented programming. It allows a class to inherit attributes and methods from another class, promoting code reuse and establishing a relationship between classes.

Understanding Inheritance

In Java, inheritance enables a class (called a subclass or derived class) to inherit the properties and behaviors of another class (called a superclass or parent class). This creates an "is-a" relationship between the classes.

Inheritance Terminology

Parent Class (Superclass)
  • The class being inherited from
  • Contains common attributes and methods
  • More general in nature
  • Base of the inheritance hierarchy
Child Class (Subclass)
  • The class that inherits
  • Inherits attributes and methods
  • Can add its own unique features
  • More specialized than parent

Basic Syntax of Inheritance

In Java, inheritance is implemented using the extends keyword. Here's the basic syntax:

java
1// Parent class
2class Animal {
3 // Fields and methods
4}
5
6// Child class inheriting from Animal
7class Dog extends Animal {
8 // Additional fields and methods
9}

When a class extends another class, it inherits all non-private members (fields, methods, and nested classes) from the parent class.

A Complete Inheritance Example

Let's look at a complete example that demonstrates inheritance in action:

java
1// Parent class
2class Vehicle {
3 // Fields
4 protected String brand;
5 protected String model;
6 protected int year;
7
8 // Constructor
9 public Vehicle(String brand, String model, int year) {
10 this.brand = brand;
11 this.model = model;
12 this.year = year;
13 }
14
15 // Method
16 public void displayInfo() {
17 System.out.println("Brand: " + brand);
18 System.out.println("Model: " + model);
19 System.out.println("Year: " + year);
20 }
21
22 // Method
23 public void start() {
24 System.out.println("Vehicle starting...");
25 }
26}
27
28// Child class
29class Car extends Vehicle {
30 // Additional fields
31 private int numDoors;
32 private boolean isConvertible;
33
34 // Constructor
35 public Car(String brand, String model, int year, int numDoors, boolean isConvertible) {
36 // Call parent constructor using super
37 super(brand, model, year);
38
39 // Initialize child-specific fields
40 this.numDoors = numDoors;
41 this.isConvertible = isConvertible;
42 }
43
44 // Override parent method
45 @Override
46 public void displayInfo() {
47 // Call parent method
48 super.displayInfo();
49
50 // Add child-specific information
51 System.out.println("Number of doors: " + numDoors);
52 System.out.println("Convertible: " + (isConvertible ? "Yes" : "No"));
53 }
54
55 // Child-specific method
56 public void honk() {
57 System.out.println("Beep beep!");
58 }
59}
60
61// Main class to test inheritance
62public class InheritanceExample {
63 public static void main(String[] args) {
64 // Create a Car object
65 Car myCar = new Car("Toyota", "Camry", 2022, 4, false);
66
67 // Call methods
68 System.out.println("Car Information:");
69 myCar.displayInfo(); // Calls overridden method
70
71 System.out.println("
72Starting the car:");
73 myCar.start(); // Inherited from Vehicle
74
75 System.out.println("
76Honking the horn:");
77 myCar.honk(); // Car-specific method
78 }
79}
80
81/* Output:
82Car Information:
83Brand: Toyota
84Model: Camry
85Year: 2022
86Number of doors: 4
87Convertible: No
88
89Starting the car:
90Vehicle starting...
91
92Honking the horn:
93Beep beep!
94*/

Types of Inheritance in Java

Java supports several types of inheritance relationships:

Single Inheritance

A class inherits from only one superclass. This is the most common type of inheritance in Java.

class B extends A

Multilevel Inheritance

A class inherits from a class that itself inherits from another class, forming a chain.

class C extends B
class B extends A

Hierarchical Inheritance

Multiple classes inherit from the same superclass.

class B extends A
class C extends A

Multiple Inheritance (Through Interfaces)

Java doesn't support multiple inheritance with classes, but a class can implement multiple interfaces.

class C implements A, B

Important Note

Java does not support multiple inheritance with classes (a class cannot extend more than one class). This is to avoid the "diamond problem" where ambiguity can arise if multiple parent classes have methods or fields with the same name. However, Java supports multiple inheritance through interfaces.

The 'super' Keyword

The super keyword in Java is used to refer to the parent class. It has two main uses:

  1. To call the parent class constructor: super() or super(parameters)
  2. To access parent class members (fields and methods): super.memberName
java
1class Animal {
2 protected String name;
3
4 public Animal(String name) {
5 this.name = name;
6 }
7
8 public void makeSound() {
9 System.out.println("Animal makes a sound");
10 }
11}
12
13class Cat extends Animal {
14 private int age;
15
16 public Cat(String name, int age) {
17 // Call parent constructor
18 super(name);
19 this.age = age;
20 }
21
22 @Override
23 public void makeSound() {
24 // Call parent method
25 super.makeSound();
26
27 // Add child-specific behavior
28 System.out.println("Cat says: Meow!");
29 }
30
31 public void displayInfo() {
32 System.out.println("Name: " + super.name); // Access parent field
33 System.out.println("Age: " + this.age);
34 }
35}
36
37public class SuperExample {
38 public static void main(String[] args) {
39 Cat myCat = new Cat("Whiskers", 3);
40 myCat.makeSound();
41 myCat.displayInfo();
42 }
43}
44
45/* Output:
46Animal makes a sound
47Cat says: Meow!
48Name: Whiskers
49Age: 3
50*/

Method Overriding

Method overriding is a feature that allows a subclass to provide a specific implementation of a method that is already defined in its parent class. The overriding method must have the same name, return type, and parameter list as the method being overridden.

Rules for Method Overriding

  • The method must have the same name as the parent class method
  • The method must have the same parameter list
  • The return type must be the same or a subtype of the parent method's return type
  • The access level cannot be more restrictive than the parent method's access level
  • The method cannot throw new or broader checked exceptions
  • Static, final, and private methods cannot be overridden
java
1class Shape {
2 public double calculateArea() {
3 return 0.0; // Default implementation
4 }
5
6 public void display() {
7 System.out.println("This is a shape");
8 }
9}
10
11class Circle extends Shape {
12 private double radius;
13
14 public Circle(double radius) {
15 this.radius = radius;
16 }
17
18 // Override calculateArea method
19 @Override
20 public double calculateArea() {
21 return Math.PI * radius * radius;
22 }
23
24 // Override display method
25 @Override
26 public void display() {
27 System.out.println("This is a circle with radius " + radius);
28 }
29}
30
31class Rectangle extends Shape {
32 private double length;
33 private double width;
34
35 public Rectangle(double length, double width) {
36 this.length = length;
37 this.width = width;
38 }
39
40 // Override calculateArea method
41 @Override
42 public double calculateArea() {
43 return length * width;
44 }
45
46 // Override display method
47 @Override
48 public void display() {
49 System.out.println("This is a rectangle with length " + length + " and width " + width);
50 }
51}
52
53public class OverridingExample {
54 public static void main(String[] args) {
55 Shape shape = new Shape();
56 Circle circle = new Circle(5.0);
57 Rectangle rectangle = new Rectangle(4.0, 6.0);
58
59 System.out.println("Shape area: " + shape.calculateArea());
60 shape.display();
61
62 System.out.println("Circle area: " + circle.calculateArea());
63 circle.display();
64
65 System.out.println("Rectangle area: " + rectangle.calculateArea());
66 rectangle.display();
67 }
68}
69
70/* Output:
71Shape area: 0.0
72This is a shape
73Circle area: 78.53981633974483
74This is a circle with radius 5.0
75Rectangle area: 24.0
76This is a rectangle with length 4.0 and width 6.0
77*/

Best Practice

Always use the @Override annotation when overriding a method. This helps catch errors at compile time if the method signature doesn't match a method in the parent class.

Polymorphism through Inheritance

Polymorphism is the ability of an object to take on many forms. In Java, polymorphism allows us to perform a single action in different ways. One way to achieve polymorphism is through inheritance and method overriding.

java
1class Animal {
2 public void makeSound() {
3 System.out.println("Animal makes a sound");
4 }
5}
6
7class Dog extends Animal {
8 @Override
9 public void makeSound() {
10 System.out.println("Dog barks: Woof woof!");
11 }
12}
13
14class Cat extends Animal {
15 @Override
16 public void makeSound() {
17 System.out.println("Cat meows: Meow!");
18 }
19}
20
21class Duck extends Animal {
22 @Override
23 public void makeSound() {
24 System.out.println("Duck quacks: Quack quack!");
25 }
26}
27
28public class PolymorphismExample {
29 public static void main(String[] args) {
30 // Create an array of Animal references
31 Animal[] animals = new Animal[3];
32
33 // Assign different types of animals to the array
34 animals[0] = new Dog();
35 animals[1] = new Cat();
36 animals[2] = new Duck();
37
38 // Polymorphic calls to makeSound
39 for (Animal animal : animals) {
40 animal.makeSound(); // Calls the appropriate overridden method
41 }
42
43 // Another example of polymorphism
44 Animal myPet = new Dog(); // Animal reference, Dog object
45 myPet.makeSound(); // Calls Dog's makeSound method
46
47 myPet = new Cat(); // Now myPet refers to a Cat object
48 myPet.makeSound(); // Calls Cat's makeSound method
49 }
50}
51
52/* Output:
53Dog barks: Woof woof!
54Cat meows: Meow!
55Duck quacks: Quack quack!
56Dog barks: Woof woof!
57Cat meows: Meow!
58*/

In the example above, we're using an Animal reference variable to refer to different types of animal objects. When we call the makeSound() method, Java determines which version of the method to call based on the actual object type, not the reference type. This is known as dynamic method dispatch and is the mechanism behind runtime polymorphism.

Abstract Classes

An abstract class is a class that cannot be instantiated and is designed to be subclassed. It may contain both abstract methods (methods without a body) and concrete methods (methods with implementation).

Abstract classes are useful when you want to define a common interface for a group of related classes, but you don't want to allow instances of the parent class itself.

java
1// Abstract class
2abstract class Shape {
3 // Abstract method (no implementation)
4 public abstract double calculateArea();
5
6 // Concrete method (with implementation)
7 public void display() {
8 System.out.println("This is a shape with area: " + calculateArea());
9 }
10}
11
12// Concrete subclass
13class Circle extends Shape {
14 private double radius;
15
16 public Circle(double radius) {
17 this.radius = radius;
18 }
19
20 // Must implement abstract methods from parent
21 @Override
22 public double calculateArea() {
23 return Math.PI * radius * radius;
24 }
25}
26
27// Another concrete subclass
28class Rectangle extends Shape {
29 private double length;
30 private double width;
31
32 public Rectangle(double length, double width) {
33 this.length = length;
34 this.width = width;
35 }
36
37 // Must implement abstract methods from parent
38 @Override
39 public double calculateArea() {
40 return length * width;
41 }
42}
43
44public class AbstractClassExample {
45 public static void main(String[] args) {
46 // Shape shape = new Shape(); // Error: Cannot instantiate abstract class
47
48 Shape circle = new Circle(5.0);
49 Shape rectangle = new Rectangle(4.0, 6.0);
50
51 circle.display();
52 rectangle.display();
53 }
54}
55
56/* Output:
57This is a shape with area: 78.53981633974483
58This is a shape with area: 24.0
59*/

Key Points About Abstract Classes

  • Abstract classes cannot be instantiated
  • Abstract classes may contain abstract methods and concrete methods
  • Subclasses must implement all abstract methods unless they are also abstract
  • Abstract classes can have constructors, fields, and non-abstract methods
  • A class can extend only one abstract class (single inheritance)

The 'final' Keyword in Inheritance

The final keyword can be used in several contexts related to inheritance:

  • Final Class: A class declared as final cannot be subclassed (extended)
  • Final Method: A method declared as final cannot be overridden in subclasses
  • Final Variable: A variable declared as final cannot be reassigned after initialization
java
1// Final class - cannot be extended
2final class FinalClass {
3 public void display() {
4 System.out.println("This is a final class");
5 }
6}
7
8// This would cause a compilation error:
9// class SubClass extends FinalClass { }
10
11class Parent {
12 // Final method - cannot be overridden
13 final public void finalMethod() {
14 System.out.println("This is a final method");
15 }
16
17 // Regular method - can be overridden
18 public void regularMethod() {
19 System.out.println("This is a regular method in Parent");
20 }
21}
22
23class Child extends Parent {
24 // This would cause a compilation error:
25 // @Override
26 // public void finalMethod() { }
27
28 // Override regular method
29 @Override
30 public void regularMethod() {
31 System.out.println("This is an overridden method in Child");
32 }
33}
34
35public class FinalExample {
36 public static void main(String[] args) {
37 Parent p = new Parent();
38 Child c = new Child();
39
40 p.finalMethod();
41 p.regularMethod();
42
43 c.finalMethod(); // Inherited from Parent
44 c.regularMethod(); // Overridden in Child
45 }
46}
47
48/* Output:
49This is a final method
50This is a regular method in Parent
51This is a final method
52This is an overridden method in Child
53*/

Best Practices for Inheritance

Use Inheritance Sparingly

Follow the "is-a" relationship principle. Only use inheritance when a subclass is truly a specialized version of the superclass. Prefer composition over inheritance when appropriate.

Design for Inheritance

Document intended overridable methods. Consider making classes or methods final if they're not designed to be extended or overridden.

Respect the Liskov Substitution Principle

Ensure that objects of a subclass can replace objects of the superclass without affecting the correctness of the program.

Avoid Method Hiding

Be careful with static methods. Static methods are not overridden; they are hidden. This can lead to confusion.

Practice Exercises

  1. Create a Person class and extend it with Student and Employee classes.
  2. Implement an abstract Shape class with concrete subclasses for different shapes.
  3. Create a class hierarchy for a banking system with different types of accounts.
  4. Design a vehicle hierarchy with appropriate method overriding.
  5. Implement a simple game with different character types using inheritance.

Summary

In this tutorial, you've learned:

  • The concept of inheritance and its importance in object-oriented programming
  • How to create and use inheritance hierarchies in Java
  • The use of the extends keyword to establish inheritance relationships
  • How to use the super keyword to access parent class members
  • Method overriding and runtime polymorphism
  • Abstract classes and their role in inheritance
  • The use of the final keyword in inheritance contexts
  • Best practices for designing inheritance hierarchies

Inheritance is a powerful feature of Java that allows for code reuse and establishing relationships between classes. When used appropriately, it can lead to more maintainable and extensible code. In the next tutorial, you'll learn about interfaces, another key feature of Java that complements inheritance.

Related Tutorials

Learn about classes and objects, the building blocks of Java.

Learn more

Understand how to define and implement interfaces in Java.

Learn more

Master the principles of OOP in Java.

Learn more