Introduction
Hello! In our last lesson, we covered how Java code is packaged into JAR files and how the JVM uses the classpath to find and load that code. We saw how build tools like Gradle automate this process, allowing you to manage complex applications and their dependencies.
Today, we'll shift our focus from packaging code to designing it. We'll explore two fundamental concepts in Java for creating flexible and maintainable systems: interfaces and abstract classes. Your goal for this lesson is to learn how to define and use both, and more importantly, to understand the design principles that guide when to choose one over the other.
This knowledge is not just academic; it's the bedrock of frameworks like Spring. The principles of abstraction and dependency inversion, which we'll touch on, are what make the Spring IoC container possible. Your experience with system design will be valuable here, as we'll be discussing contracts, inheritance, and composition.
What is an Abstract Class?
An abstract class is a blueprint for other classes. It provides a common base for a family of related objects. The two most important characteristics of an abstract class are:
- It cannot be instantiated directly. You can't create an object of an abstract class.
- It can contain both abstract methods (methods without an implementation) and concrete methods (methods with a full implementation).
A subclass that extends an abstract class must provide an implementation for all of its parent's abstract methods, or it must also be declared abstract. This allows the abstract class to enforce a certain structure while also sharing common code.
This is very similar to a C++ class that contains at least one pure virtual function (virtual void myMethod() = 0;), which also makes the class abstract.
To see this in action, please watch the following video. It provides a clear, code-driven explanation of abstract classes and methods.
Abstract Classes and Methods in Java Explained in 7 Minutes
The video 'Abstract Classes and Methods in Java Explained in 7 Minutes' by Coding with John is a great introduction. It covers the core concepts with a simple, practical example.
Please watch from the beginning up to the 04:22 mark. Focus on: Why you can't create an object from an abstract class. The Animal example, which illustrates a good use case. How abstract methods force subclasses to provide their own implementation. How abstract classes can also provide concrete, implemented methods that all subclasses inherit.
As the video explained, abstract classes are perfect for modeling "is-a" relationships where you want to share code. For example, a Car is a Vehicle, and a Motorcycle is a Vehicle. They share common attributes (like speed) and behaviors (like startEngine()), but some behaviors (accelerate()) might be specific to each type. An abstract Vehicle class is the ideal way to model this.
What is an Interface?
An interface is a contract. It defines a set of capabilities or behaviors that a class must provide, without specifying anything about its implementation. An interface defines what a class can do, but not how it does it.
Key characteristics (in the traditional sense, before Java 8):
- It consists only of method signatures (without bodies) and constant definitions.
- A class can implement multiple interfaces. This is how Java achieves a form of multiple inheritance of type.
- Any class that implements an interface must provide an implementation for all of its methods.
A common design heuristic is to use an interface when you can describe a capability with the phrase "is-capable-of". For example, Bird and Airplane are unrelated objects, but both are capable of flying. You could define a Flyable interface that they both implement.
This is fundamentally about defining a public API. It separates the contract from the implementation, a principle you're very familiar with in system design.
The Stack Overflow answer below gives a simple but effective analogy of an interface as a contract.
Interfaces vs. Abstract Classes on Stack Overflow
This highly-rated answer from Stack Overflow, titled 'What is the difference between an interface and abstract class?', frames the concepts perfectly.
Read the first two short sections, 'Interfaces' and 'Abstract classes'. The pseudocode clearly illustrates the interface as a 'contract' and the abstract class as a 'fill in the blanks' template.
The Modern Interface: Java 8 and Beyond
The line between interfaces and abstract classes became more nuanced with the release of Java 8. To allow for the evolution of existing APIs (like the Java Collections Framework) without breaking millions of lines of code, interfaces were given two new capabilities: default and static methods.
defaultmethods: These are methods in an interface that have a default implementation. A class that implements the interface can either use the default implementation or override it with its own. This solved the problem of adding new methods to an interface without forcing all existing implementing classes to be modified.staticmethods: These are utility methods that are associated with the interface itself, rather than with any specific object of an implementing class. They are called using the interface name (e.g.,MyInterface.myStaticMethod()).
The following video explains the "why" and "how" of these powerful features.
8.22 Interface in Java 8 Default , Static Methods | New features
The video 'Interface in Java 8' by Telusko clearly explains the motivation and syntax for default and static methods in interfaces. It also touches on how Java resolves potential conflicts.
Please watch the following segments: Motivation for Change (02:19 - 05:44): Understand the backward-compatibility problem that default methods solve. default Method Syntax (05:44 - 07:24): See how the default keyword is used to provide an implementation inside an interface. static Methods in Interfaces (16:14 - 17:41): Learn how static methods work and how they are called.
Choosing Between an Interface and an Abstract Class
So, when should you use which? With default methods, interfaces can now provide behavior, just like abstract classes. However, the fundamental design considerations remain.
This table provides a concise summary of the technical differences:
Beyond the syntax, here are the key design principles to guide your choice:
-
"Is-A" vs. "Is-Capable-Of":
- Use an abstract class for a strong "is-a" relationship, when objects are of the same family and you want to share code. (e.g.,
Dogis anAnimal). - Use an interface for a weaker "is-capable-of" relationship, to define a capability that unrelated classes can share. (e.g.,
CarandPersoncan both implementSerializable).
- Use an abstract class for a strong "is-a" relationship, when objects are of the same family and you want to share code. (e.g.,
-
State vs. Behavior:
- If you need to share common state (instance variables) among subclasses, you must use an abstract class. Interfaces are primarily for defining behavior. While they can have
static finalconstants, they cannot have per-instance state.
- If you need to share common state (instance variables) among subclasses, you must use an abstract class. Interfaces are primarily for defining behavior. While they can have
-
Multiple Inheritance:
- A class can only extend one parent class (abstract or not). If a class needs to take on behaviors from multiple sources, you must use interfaces. A class can implement any number of interfaces.
-
Public API vs. Implementation Helper:
- Think of an interface as a pure public API contract. It's what you expose to the world (or other parts of your system).
- Think of an abstract class as a helpful starting point for implementing that contract. It can provide a skeletal implementation, which concrete classes can then complete. The Spring Framework uses this pattern extensively.
The following article provides excellent heuristics and examples for making this design choice.
Using an Interface vs. Abstract Class in Java
The article 'Using an Interface vs. Abstract Class in Java' from Baeldung is a classic resource that summarizes the decision-making process well.
Please read sections 4 ('When to Use an Interface') and 5 ('When to Use an Abstract Class'). Pay close attention to the simple rules of thumb and the Sender/Vehicle code examples.
Given your background in system design, it's also worth noting how these concepts map to common design patterns.
- Interfaces are central to patterns like Strategy, Command, and Factory Method, where you need to decouple the client from the concrete implementation.
- Abstract classes are the cornerstone of the Template Method pattern, where an algorithm's skeleton is defined in the abstract class, and subclasses override specific steps.
Conclusion
In this lesson, you've explored the roles of abstract classes and interfaces in Java. You've learned not only their syntax but also the design considerations that determine when to use them.
Here are the key takeaways:
- Abstract Classes are for sharing code and state among closely related classes in an inheritance hierarchy ("is-a" relationship). They serve as a base implementation.
- Interfaces are for defining a contract of capabilities that can be implemented by any class, regardless of where it is in the class hierarchy ("is-capable-of" relationship). They are crucial for creating loosely-coupled systems.
defaultmethods in Java 8+ allow interfaces to provide implementation details, primarily to ensure backward compatibility as APIs evolve.- The choice between them is a key design decision. Consider whether you need to share state, if you need multiple "inheritance" of type, and whether you are defining a public contract or a base implementation.
In our next lesson, we will cover Java generics. You'll see how generics work hand-in-hand with interfaces and classes to create highly reusable and type-safe components, like the ubiquitous List<String> or Map<Key, Value>.