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:
1// Parent class2class Animal {3 // Fields and methods4}56// Child class inheriting from Animal7class Dog extends Animal {8 // Additional fields and methods9}
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:
1// Parent class2class Vehicle {3 // Fields4 protected String brand;5 protected String model;6 protected int year;78 // Constructor9 public Vehicle(String brand, String model, int year) {10 this.brand = brand;11 this.model = model;12 this.year = year;13 }1415 // Method16 public void displayInfo() {17 System.out.println("Brand: " + brand);18 System.out.println("Model: " + model);19 System.out.println("Year: " + year);20 }2122 // Method23 public void start() {24 System.out.println("Vehicle starting...");25 }26}2728// Child class29class Car extends Vehicle {30 // Additional fields31 private int numDoors;32 private boolean isConvertible;3334 // Constructor35 public Car(String brand, String model, int year, int numDoors, boolean isConvertible) {36 // Call parent constructor using super37 super(brand, model, year);3839 // Initialize child-specific fields40 this.numDoors = numDoors;41 this.isConvertible = isConvertible;42 }4344 // Override parent method45 @Override46 public void displayInfo() {47 // Call parent method48 super.displayInfo();4950 // Add child-specific information51 System.out.println("Number of doors: " + numDoors);52 System.out.println("Convertible: " + (isConvertible ? "Yes" : "No"));53 }5455 // Child-specific method56 public void honk() {57 System.out.println("Beep beep!");58 }59}6061// Main class to test inheritance62public class InheritanceExample {63 public static void main(String[] args) {64 // Create a Car object65 Car myCar = new Car("Toyota", "Camry", 2022, 4, false);6667 // Call methods68 System.out.println("Car Information:");69 myCar.displayInfo(); // Calls overridden method7071 System.out.println("72Starting the car:");73 myCar.start(); // Inherited from Vehicle7475 System.out.println("76Honking the horn:");77 myCar.honk(); // Car-specific method78 }79}8081/* Output:82Car Information:83Brand: Toyota84Model: Camry85Year: 202286Number of doors: 487Convertible: No8889Starting the car:90Vehicle starting...9192Honking 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:
- To call the parent class constructor:
super()
orsuper(parameters)
- To access parent class members (fields and methods):
super.memberName
1class Animal {2 protected String name;34 public Animal(String name) {5 this.name = name;6 }78 public void makeSound() {9 System.out.println("Animal makes a sound");10 }11}1213class Cat extends Animal {14 private int age;1516 public Cat(String name, int age) {17 // Call parent constructor18 super(name);19 this.age = age;20 }2122 @Override23 public void makeSound() {24 // Call parent method25 super.makeSound();2627 // Add child-specific behavior28 System.out.println("Cat says: Meow!");29 }3031 public void displayInfo() {32 System.out.println("Name: " + super.name); // Access parent field33 System.out.println("Age: " + this.age);34 }35}3637public class SuperExample {38 public static void main(String[] args) {39 Cat myCat = new Cat("Whiskers", 3);40 myCat.makeSound();41 myCat.displayInfo();42 }43}4445/* Output:46Animal makes a sound47Cat says: Meow!48Name: Whiskers49Age: 350*/
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
1class Shape {2 public double calculateArea() {3 return 0.0; // Default implementation4 }56 public void display() {7 System.out.println("This is a shape");8 }9}1011class Circle extends Shape {12 private double radius;1314 public Circle(double radius) {15 this.radius = radius;16 }1718 // Override calculateArea method19 @Override20 public double calculateArea() {21 return Math.PI * radius * radius;22 }2324 // Override display method25 @Override26 public void display() {27 System.out.println("This is a circle with radius " + radius);28 }29}3031class Rectangle extends Shape {32 private double length;33 private double width;3435 public Rectangle(double length, double width) {36 this.length = length;37 this.width = width;38 }3940 // Override calculateArea method41 @Override42 public double calculateArea() {43 return length * width;44 }4546 // Override display method47 @Override48 public void display() {49 System.out.println("This is a rectangle with length " + length + " and width " + width);50 }51}5253public 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);5859 System.out.println("Shape area: " + shape.calculateArea());60 shape.display();6162 System.out.println("Circle area: " + circle.calculateArea());63 circle.display();6465 System.out.println("Rectangle area: " + rectangle.calculateArea());66 rectangle.display();67 }68}6970/* Output:71Shape area: 0.072This is a shape73Circle area: 78.5398163397448374This is a circle with radius 5.075Rectangle area: 24.076This is a rectangle with length 4.0 and width 6.077*/
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.
1class Animal {2 public void makeSound() {3 System.out.println("Animal makes a sound");4 }5}67class Dog extends Animal {8 @Override9 public void makeSound() {10 System.out.println("Dog barks: Woof woof!");11 }12}1314class Cat extends Animal {15 @Override16 public void makeSound() {17 System.out.println("Cat meows: Meow!");18 }19}2021class Duck extends Animal {22 @Override23 public void makeSound() {24 System.out.println("Duck quacks: Quack quack!");25 }26}2728public class PolymorphismExample {29 public static void main(String[] args) {30 // Create an array of Animal references31 Animal[] animals = new Animal[3];3233 // Assign different types of animals to the array34 animals[0] = new Dog();35 animals[1] = new Cat();36 animals[2] = new Duck();3738 // Polymorphic calls to makeSound39 for (Animal animal : animals) {40 animal.makeSound(); // Calls the appropriate overridden method41 }4243 // Another example of polymorphism44 Animal myPet = new Dog(); // Animal reference, Dog object45 myPet.makeSound(); // Calls Dog's makeSound method4647 myPet = new Cat(); // Now myPet refers to a Cat object48 myPet.makeSound(); // Calls Cat's makeSound method49 }50}5152/* 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.
1// Abstract class2abstract class Shape {3 // Abstract method (no implementation)4 public abstract double calculateArea();56 // Concrete method (with implementation)7 public void display() {8 System.out.println("This is a shape with area: " + calculateArea());9 }10}1112// Concrete subclass13class Circle extends Shape {14 private double radius;1516 public Circle(double radius) {17 this.radius = radius;18 }1920 // Must implement abstract methods from parent21 @Override22 public double calculateArea() {23 return Math.PI * radius * radius;24 }25}2627// Another concrete subclass28class Rectangle extends Shape {29 private double length;30 private double width;3132 public Rectangle(double length, double width) {33 this.length = length;34 this.width = width;35 }3637 // Must implement abstract methods from parent38 @Override39 public double calculateArea() {40 return length * width;41 }42}4344public class AbstractClassExample {45 public static void main(String[] args) {46 // Shape shape = new Shape(); // Error: Cannot instantiate abstract class4748 Shape circle = new Circle(5.0);49 Shape rectangle = new Rectangle(4.0, 6.0);5051 circle.display();52 rectangle.display();53 }54}5556/* Output:57This is a shape with area: 78.5398163397448358This is a shape with area: 24.059*/
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
1// Final class - cannot be extended2final class FinalClass {3 public void display() {4 System.out.println("This is a final class");5 }6}78// This would cause a compilation error:9// class SubClass extends FinalClass { }1011class Parent {12 // Final method - cannot be overridden13 final public void finalMethod() {14 System.out.println("This is a final method");15 }1617 // Regular method - can be overridden18 public void regularMethod() {19 System.out.println("This is a regular method in Parent");20 }21}2223class Child extends Parent {24 // This would cause a compilation error:25 // @Override26 // public void finalMethod() { }2728 // Override regular method29 @Override30 public void regularMethod() {31 System.out.println("This is an overridden method in Child");32 }33}3435public class FinalExample {36 public static void main(String[] args) {37 Parent p = new Parent();38 Child c = new Child();3940 p.finalMethod();41 p.regularMethod();4243 c.finalMethod(); // Inherited from Parent44 c.regularMethod(); // Overridden in Child45 }46}4748/* Output:49This is a final method50This is a regular method in Parent51This is a final method52This is an overridden method in Child53*/
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
- Create a
Person
class and extend it withStudent
andEmployee
classes. - Implement an abstract
Shape
class with concrete subclasses for different shapes. - Create a class hierarchy for a banking system with different types of accounts.
- Design a vehicle hierarchy with appropriate method overriding.
- 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 moreUnderstand how to define and implement interfaces in Java.
Learn moreMaster the principles of OOP in Java.
Learn more