3.4 Abstract Classes and Interfaces
Key Takeaways
- Abstract classes declare shared state and partial implementations with constructors, but cannot be directly instantiated.
- Interfaces define contracts supporting multiple inheritance of type, containing public abstract methods, default methods, static methods, and private helper methods.
- Default method conflicts from multiple interface inheritance must be explicitly resolved in the implementing class using the InterfaceName.super.method() syntax.
- Under the class-always-wins rule, an inherited concrete superclass method always takes precedence over an interface default method of the same signature.
Abstract Classes and Interfaces
In Java SE 21, abstraction is realized through two distinct constructs: abstract classes and interfaces. While both prohibit direct instantiation and define contracts for subclasses, they serve different architectural roles and follow distinct inheritance and member accessibility rules.
Abstract Classes vs. Interfaces: Architectural Comparison
| Dimension | Abstract Class | Interface |
|---|---|---|
| Inheritance Model | Single inheritance (class A extends B) | Multiple inheritance of type (class A implements B, C) |
| Instance State / Fields | Can declare mutable instance fields (private int x;) | No instance state; variables are implicitly public static final |
| Constructors | Supported (invoked via super(...) during subclass instantiation) | Prohibited (interfaces cannot declare constructors) |
| Method Types Supported | Abstract, concrete instance, static, final, private | Abstract, default, static, private, private static |
| Default Method Visibility | Package-private by default (any modifier allowed) | Implicitly public (unless explicitly declared private) |
| Primary Design Purpose | Shared identity, base state, and partial implementation | Capability contracts and decoupled API specifications |
Abstract Class Mechanics and Rules
An abstract class is declared with the abstract keyword and serves as an incomplete base class.
public abstract class AbstractTaskProcessor {
private final String processorName;
protected int processedCount = 0;
// Abstract classes support constructors
public AbstractTaskProcessor(String processorName) {
this.processorName = processorName;
}
public String getProcessorName() { return processorName; }
// Abstract method: declares contract without body
public abstract void processPayload(String payload);
// Concrete template method
public void execute(String payload) {
System.out.println("Initializing " + processorName);
processPayload(payload);
processedCount++;
}
}
Critical Rules for Abstract Classes and Methods
- Instantiation Prohibition: You cannot instantiate an abstract class directly using
new AbstractClass(). - Abstract Method Body: An abstract method consists of a method signature terminated by a semicolon (
;). It cannot have a method body enclosed in braces{}. - Class Declaration Requirement: If a class contains at least one abstract method (declared or inherited without implementation), the class itself must be declared
abstract. - Subclass Obligations: The first concrete (non-abstract) subclass in an inheritance chain must provide concrete method bodies for all inherited abstract methods. Intermediate abstract subclasses may implement some, all, or none of the inherited abstract methods, and may introduce new abstract methods.
- Illegal Modifier Combinations: Abstract methods cannot be declared:
abstract final:finalprevents overriding, whileabstractdemands overriding.abstract static:staticmethods cannot be overridden dynamically.abstract private:privatemethods are invisible to subclasses and cannot be implemented.abstract synchronizedorabstract native: Synchronization and native implementations apply to concrete execution bodies, not abstract signatures.
Modern Interface Architecture in Java SE 21
Since Java 8 and Java 9, interfaces support rich contracts containing five distinct categories of methods:
public interface MessageService {
// 1. Constant Field: implicitly public static final
int MAX_RETRIES = 3;
// 2. Abstract Method: implicitly public abstract
void sendMessage(String recipient, String message);
// 3. Default Method (Java 8+): public instance method with default implementation
default void sendWithRetry(String recipient, String message) {
for (int i = 0; i < MAX_RETRIES; i++) {
try {
logAttempt(recipient, i + 1);
sendMessage(recipient, message);
return;
} catch (Exception e) {
logFailure(recipient, e);
}
}
}
// 4. Static Method (Java 8+): utility method attached to interface namespace
static MessageService createConsoleService() {
return (recipient, msg) -> System.out.println("To " + recipient + ": " + msg);
}
// 5. Private Instance Method (Java 9+): helper method shared between default methods
private void logAttempt(String recipient, int attempt) {
System.out.println("[ATTEMPT " + attempt + "] Sending to " + recipient);
}
// 6. Private Static Method (Java 9+): helper method shared between static methods
private static void logFailure(String recipient, Exception e) {
System.err.println("[FAILED] " + recipient + ": " + e.getMessage());
}
}
Interface Member Rules and Visibility
- Interface Fields: All fields declared in an interface are implicitly
public static final. They must be initialized at declaration time. Marking themprivate,protected, or non-final causes a compile-time error. - Default Methods: Marked with the
defaultkeyword and provide a full method body. They are implicitlypublic. Implementing classes inherit default methods and can choose to use them directly or override them. Default methods cannot be markedstatic,final, orabstract. - Static Methods in Interfaces: Belong exclusively to the interface. Interface static methods are NOT inherited by implementing classes or subinterfaces. They must be invoked using the explicit syntax:
InterfaceName.staticMethodName(). - Private Methods: Introduced in Java 9 to encapsulate helper code without exposing public default or static APIs. Can be
private(instance helper for default methods) orprivate static(static helper for static/default methods).
Resolving Multiple Inheritance Conflicts (The Diamond Problem)
Because a Java class can implement multiple interfaces, conflicting default methods can introduce ambiguity.
interface TransmitterA {
default void transmit() {
System.out.println("Transmitting via Channel A");
}
}
interface TransmitterB {
default void transmit() {
System.out.println("Transmitting via Channel B");
}
}
// COMPILE ERROR if transmit() is not overridden!
public class RadioStation implements TransmitterA, TransmitterB {
// Implementing class MUST resolve the conflict explicitly
@Override
public void transmit() {
// Option 1: Completely custom logic
System.out.println("Custom Station Transmission");
// Option 2: Delegate to a specific interface default method using InterfaceName.super
TransmitterA.super.transmit();
}
}
Priority Rules for Default Method Resolution
- The "Class Always Wins" Rule: If a superclass provides a concrete implementation of a method, and an implemented interface provides a
defaultmethod with the exact same signature, the superclass implementation always takes precedence. The interface default method is completely ignored and no conflict occurs.
class BaseLogger {
public void log(String msg) {
System.out.println("BaseLogger: " + msg);
}
}
interface InterfaceLogger {
default void log(String msg) {
System.out.println("InterfaceLogger: " + msg);
}
}
class AppLogger extends BaseLogger implements InterfaceLogger {
// No compile error! BaseLogger.log() wins automatically.
}
- The "Sub-Interface Wins" Rule: If Interface B extends Interface A and overrides A's default method, an implementing class that implements both A and B will automatically inherit B's more specific implementation.
- Explicit Conflict Resolution: If two unrelated interfaces provide default methods with the same signature, the implementing class must override the method, or a compile-time error occurs. Within the override, the developer may delegate using
InterfaceName.super.methodName().
Common 1Z0-830 Exam Traps
- Calling Interface Static Methods on Implementing Classes: Calling
AppLogger.createConsoleService()ormyInstance.createConsoleService()wherecreateConsoleServiceis a static method inMessageServicecauses a compile error. You MUST writeMessageService.createConsoleService(). - Default Method Super Syntax: The syntax to delegate to an interface default method is strictly
InterfaceName.super.method(). Writingsuper.method()orInterfaceName.method()fails compilation. - Uninitialized Interface Constants: Declaring
int TIMEOUT;inside an interface causes a compile-time error because interface fields are implicitlyfinaland must be initialized. - Abstract Class with Zero Abstract Methods: An abstract class does not need to have any abstract methods; it is perfectly valid to create an abstract class solely to prevent direct instantiation.
Consider the following interface definitions and class declaration:
Which code at Line 1 correctly resolves the default method conflict?interface Reader {
default String read() { return "Reading text"; }
}
interface Scanner {
default String read() { return "Scanning barcode"; }
}
class SmartDevice implements Reader, Scanner {
// Line 1
}
Examine the following interface and implementing class:
Which statement at Line X is valid to invoke isValid?interface Validator {
static boolean isValid(String s) {
return s != null && !s.isBlank();
}
}
public class FormValidator implements Validator {
public void check(String data) {
// Line X
}
}
Consider the following class and interface definition:
What is the result when executing class BaseService {
public String process() { return "BaseService"; }
}
interface ServiceContract {
default String process() { return "ServiceContract"; }
}
class OrderService extends BaseService implements ServiceContract {}
new OrderService().process()?
Which of the following method declarations is ILLEGAL inside a top-level interface in Java SE 21?