Hello! Welcome to the third lesson in our journey through Object-Oriented Programming foundations.
In the previous lesson, we focused on encapsulation and the use of access modifiers (public, private, default). We ended by noting that the protected modifier is specifically designed for inheritance, allowing subclasses to access members of their parent class. Today, we'll explore that relationship fully.
Our learning outcome is to design class hierarchies using inheritance, abstract classes, and interfaces. This is a fundamental skill in Low-Level Design. In an interview, creating a well-structured class hierarchy demonstrates that you can build systems that are logical, maintainable, and flexible.
1. Inheritance: The "Is-A" Relationship
At its core, inheritance allows a new class (the subclass or child class) to be based on an existing class (the superclass or parent class). The subclass inherits the attributes and methods of its superclass, forming an "is-a" relationship. For example, a Manager is a type of Employee.
This mechanism is central to creating logical hierarchies and promoting code reuse. Let's dive into the theory with a foundational reading.
Class Hierarchies and Inheritance
This document provides a thorough explanation of class hierarchies and inheritance. It will ground you in the core concepts.
Read the sections titled 'Organizing Classes' and 'Inheritance'. Focus on: The concept of the 'is-a' relationship. How to use the extends keyword in Java. How subclasses inherit fields and methods from superclasses. The concept of method overriding and using the super keyword to call a parent class's method or constructor.
To summarize the key points from the reading:
- Code Reusability: You define common attributes and behaviors in a superclass, and multiple subclasses can reuse them without duplication. The classic example is a
Personclass withnameandaddress, which can be extended byEmployeeandCustomer. - Type Hierarchy: Inheritance creates a clear and logical structure. A
Carobject is also aVehicleobject. This relationship is crucial for polymorphism, which we will cover in a future lesson. - Overriding: A subclass can provide its own specific implementation of a method that it inherits from its superclass. For instance, a
SavingsAccountmight override thewithdrawmethod of aBankAccountto add a fee. - The
superkeyword: This keyword gives you access to the immediate parent class. You can usesuper.methodName()to call the parent's method from within an overridden method, orsuper()to call the parent's constructor.
A quick note on access modifiers: as we touched upon last lesson, and as the reading details in its "How Are Access Modifiers Affected By Inheritance?" section, protected members of a superclass are directly accessible to its subclasses. This is how you allow child classes to interact with the parent's state in a controlled way, without making that state public.
2. Abstract Classes: Defining a Template
What if you have a concept that is too general to be instantiated on its own? For instance, it makes sense to create a Dog or Cat object, but does it make sense to create a generic Animal object? What kind of animal would it be?
This is where abstract classes come in. An abstract class serves as a template for other classes. It cannot be instantiated itself, but it can be extended.
Abstract Classes and Methods in Java Explained in 7 Minutes
This short video from the 'Coding with John' channel provides a clear, concise explanation of abstract classes and abstract methods.
Watch the video from 00:28 to 04:14. Pay attention to: What the abstract keyword does to a class. What an abstract method is and why it has no body. How abstract classes enforce a common structure on their subclasses.
Key characteristics of abstract classes:
- They are declared with the
abstractkeyword. - They cannot be instantiated directly using
new. - They can contain both abstract methods (with no implementation) and concrete methods (with a full implementation).
- Any concrete (non-abstract) class that extends an abstract class must provide an implementation for all inherited abstract methods.
By using an abstract class, you are saying: "Any class that is a type of X must have these specific behaviors and can optionally use this shared code."
3. Interfaces: Defining a Contract
While inheritance defines what an object is, an interface defines what an object can do. An interface is a contract that specifies a set of method signatures. Any class that implements an interface agrees to provide implementations for all the methods defined in that interface.
The most significant difference from class inheritance is that a Java class can implement multiple interfaces but can only extend one superclass.
Let's see a practical example of how to use both abstract classes and interfaces together to build a flexible design.
Java Abstract Class VS. Interface - Example Using Both and an Explanation - APPFICIAL
This video from 'Appficial' clearly demonstrates a scenario where a class both extends a parent and implements multiple interfaces.
Watch the following segments: 00:00 - 00:41: The core difference: extend one, implement many. 02:26 - 04:42: See how the Animal abstract class is made to implement Alive and Measurable interfaces. 07:00 - 09:19: This part is crucial. Notice how unrelated classes (Tree, VideoGameCharacter) can all implement the Alive interface, allowing them to be treated polymorphically.
An interface defines a "can-do" or "has-a-capability" relationship. A Car and a Person are unrelated, but both could implement a Sellable interface. A Thread and a TimerTask are different, but both implement the Runnable interface because they both define a task that can be run.
This is powerfully demonstrated in the Java Development Kit (JDK) itself. For example, the Java Collection Framework is built on a rich hierarchy of interfaces and classes.

4. Abstract Class vs. Interface: Making the Design Choice
In a system design interview, you'll need to justify your design choices. When should you use an abstract class, and when is an interface more appropriate?

Let's watch a final short clip that directly compares the two.
Abstract Classes and Methods in Java Explained in 7 Minutes
This segment neatly summarizes the key differences and helps guide the decision-making process.
Watch from 04:14 to the end. The video highlights the main distinctions regarding multiple inheritance, fields, and intended use cases.
Here's a summary to guide your decision:
Use an Abstract Class when:
- You want to share code among several closely related classes.
- You expect that classes that extend your abstract class have many common methods or fields, or require access modifiers other than
public(e.g.,protected). - You are defining the fundamental identity of an object and want to establish a strong "is-a" relationship. (e.g.,
AbstractVehicle,AbstractAccount).
Use an Interface when:
- You expect that unrelated classes will implement the interface. For example,
ComparableandCloneableare implemented by many unrelated classes. - You want to specify the behavior of a particular data type, but you are not concerned about who implements its behavior.
- You want to take advantage of multiple inheritance of type.
Test your understanding!
You are designing a system for a shipping company. You need to model different types of packages: Box, Envelope, and Crate. All of them have a trackingNumber and a weight. They all must be able to calculate their shippingCost.
Additionally, some items, like fragile Boxes or important Envelopes, need to be insurable. These items must have a method to calculateInsurancePremium(). A Crate is never insurable.
How would you use abstract classes and/or interfaces to model this? Justify your choice.
Show answer
A good approach would be to use both an abstract class and an interface.
-
PackageAbstract Class:
Create an abstract class namedPackage.- Fields: It would contain the common fields
trackingNumberandweight. These could beprotectedso subclasses can access them. - Abstract Method: It would have an abstract method
public abstract double calculateShippingCost();. This forces every concrete package type to implement its own cost calculation logic. - Concrete Subclasses:
Box,Envelope, andCratewould allextend Package.
Justification: An abstract class is perfect here because
Box,Envelope, andCrateare all closely related types ofPackage. They share common state (trackingNumber,weight) and a required behavior (calculateShippingCost). This is a clear "is-a" relationship. - Fields: It would contain the common fields
-
InsurableInterface:
Create an interface namedInsurable.- Abstract Method: It would define one method:
public double calculateInsurancePremium();. - Implementing Classes: The
BoxandEnvelopeclasses wouldimplement Insurable. TheCrateclass would not.
Justification: An interface is ideal because "being insurable" is a capability or a contract that only some packages have. It's a "can-do" property. A
Boxis aPackage, and it can beInsurable. Using an interface allows you to apply this behavior to select classes without forcing it on the entirePackagehierarchy. - Abstract Method: It would define one method:
5. Application in a Low-Level Design
Let's see how these concepts are applied in a real LLD problem. We'll look at the design for a system like Stack Overflow. User-generated content comes in different forms: questions, answers, and comments. This is a perfect candidate for a class hierarchy.
This case study for designing Stack Overflow provides a great example of an inheritance hierarchy.
Read the sections 'Class Definitions' and 'Class Relationships', focusing on the inheritance structure. Notice how Content is an abstract base class, which is extended by the abstract Post class (for things that can be voted on), which is then extended by the concrete Question and Answer classes.
The Stack Overflow design uses a hierarchy to great effect:
Content(abstract): The base for all content, containing common fields likeauthorandcreationTime.Post(abstract, extendsContent): A more specific type of content that can be voted on. It adds voting logic.Question(concrete, extendsPost): A post with a title and tags.Answer(concrete, extendsPost): A post that belongs to a question.Comment(concrete, extendsContent): Simple content that cannot be voted on.
This structure is logical and prevents code duplication by placing shared functionality in the appropriate parent class.
Conclusion
Today, we explored the tools for building structured and logical class hierarchies in Java. Designing these hierarchies is a creative process of identifying commonalities and separating concerns.
Key Takeaways:
- Inheritance (
extends) creates an "is-a" relationship, allowing a subclass to inherit state and behavior from a single superclass. It's for building a hierarchy of related objects. - Abstract Classes serve as non-instantiable templates for a family of classes. They can provide shared code (concrete methods) and enforce common behavior (abstract methods).
- Interfaces (
implements) define a "can-do" contract. A class can implement multiple interfaces, allowing objects from different hierarchies to share common behaviors. - The choice between them is a key design decision: use abstract classes for shared identity and code within a family; use interfaces to define capabilities that can be applied across different families.
In this lesson, we saw how a subclass can override a method from its superclass. This is a powerful feature that enables runtime polymorphism. In our next lesson, we will focus entirely on this concept: implementing runtime polymorphism using method overriding.