06 - Interfaces, Abstract Classes, Records, and Sealed Classes
A complete beginner-to-advanced guide to Java's abstraction and modern type modeling, aligned with the Oracle Certified Professional: Java SE 21 Developer (1Z0-830) exam objectives. Heavy focus on Java 21 features.
Table of Contents
- Abstraction Overview
- Interfaces
- Interface Members
- Default Methods
- Static Interface Methods
- Private Interface Methods
- The Diamond Problem
- Abstract Classes
- Interface vs Abstract Class
- Records (Java 16+)
- Record Patterns (Java 21)
- Sealed Classes (Java 17+)
- Sealed + Pattern Matching (Java 21)
- Certification Traps
- Common Mistakes
- Interview Questions
- Quick Revision Notes
- One-Page Cheat Sheet
1. Abstraction Overview
Abstraction means exposing what an object does while hiding how. Java offers several tools:
| Tool | Use When |
|---|---|
| Interface | Define a contract; multiple inheritance of type. |
| Abstract class | Share common state + partial implementation. |
| Record | Model immutable data concisely. |
| Sealed class | Restrict which classes can extend/implement. |
2. Interfaces
An interface is a contract — a set of method signatures a class promises to implement using implements.
interface Drawable {
void draw(); // public abstract by default
}
class Circle implements Drawable {
@Override
public void draw() { System.out.println("Drawing circle"); }
}
Drawable d = new Circle(); // program to the interface
d.draw();
Key Facts
| Fact | Detail |
|---|---|
| Multiple inheritance | A class can implement many interfaces. |
| Interface extends interface(s) | An interface can extend multiple interfaces. |
| Cannot be instantiated | new Drawable() is illegal (except anonymous classes). |
Implicit public abstract | Methods are public abstract unless default/static/private. |
| Reference type | You can declare variables of an interface type. |
interface A {}
interface B {}
interface C extends A, B {} // interface multiple inheritance — OK
class D implements A, B {} // class implements multiple — OK
3. Interface Members
Modern interfaces (Java 8+) can hold much more than abstract methods.
| Member | Modifier | Since |
|---|---|---|
| Abstract method | implicitly public abstract | 1.0 |
| Constant field | implicitly public static final | 1.0 |
| Default method | default | Java 8 |
| Static method | static | Java 8 |
| Private method | private | Java 9 |
| Private static method | private static | Java 9 |
interface Config {
int MAX = 100; // public static final (constant)
void process(); // public abstract
default void log() { // Java 8
System.out.println("Default log");
}
static Config create() { // Java 8
return new ConfigImpl();
}
private void helper() { } // Java 9
private static void util() { } // Java 9
}
Constant Trap
interface Limits {
int MAX = 100; // this is public static final implicitly
// MAX = 200; // ERROR: cannot reassign a final
}
All interface fields are constants (
public static final). You cannot have instance fields in an interface.
4. Default Methods
A default method provides a method body inside an interface, so implementing classes inherit it without writing it.
interface Vehicle {
void start(); // abstract
default void honk() { // default with body
System.out.println("Beep beep!");
}
}
class Car implements Vehicle {
@Override public void start() { System.out.println("Car started"); }
// honk() inherited automatically
}
Why Default Methods Exist
They allow adding new methods to interfaces without breaking existing implementations (backward compatibility — e.g., List.sort(), Collection.stream()).
Rules
| Rule | Detail |
|---|---|
| Has a body | Marked with default. |
| Can be overridden | Implementing classes may override. |
public | Always public (cannot be private/protected). |
Not static/final | Cannot combine default with static. |
| Calling | InterfaceName.super.method() to invoke a specific default. |
5. Static Interface Methods
Static methods belong to the interface itself and are not inherited by implementing classes.
interface MathOps {
static int square(int x) { return x * x; }
}
// Call via interface name only:
int result = MathOps.square(5); // 25
Trap: Not Inherited
class Calc implements MathOps {}
// Calc.square(5); // ERROR: static interface method not inherited
MathOps.square(5); // OK
Unlike static methods in classes, interface static methods are never inherited by implementing classes or subinterfaces.
6. Private Interface Methods
Since Java 9, interfaces can have private methods to share code among default/static methods without exposing it.
interface Logger {
default void info(String msg) { log("INFO", msg); }
default void error(String msg) { log("ERROR", msg); }
private void log(String level, String msg) { // shared helper
System.out.println("[" + level + "] " + msg);
}
}
| Private method type | Can be called by |
|---|---|
private | Default methods (instance context). |
private static | Both default and static methods. |
Private interface methods must have a body and cannot be abstract.
7. The Diamond Problem
When a class inherits the same default method from two interfaces, it must override to resolve the conflict.
interface A { default void hello() { System.out.println("A"); } }
interface B { default void hello() { System.out.println("B"); } }
class C implements A, B {
@Override
public void hello() {
A.super.hello(); // explicitly pick A's version
// or B.super.hello();
}
}
| Conflict | Resolution |
|---|---|
| Two default methods, same signature | Class must override. |
| Use a parent's version | InterfaceName.super.method(). |
| Class method vs interface default | Class wins ("class always wins" rule). |
"Class wins" rule: If a superclass provides a concrete method and an interface provides a default with the same signature, the class method takes precedence.
8. Abstract Classes
An abstract class is a partially implemented class that cannot be instantiated. It can hold state, constructors, and both abstract and concrete methods.
abstract class Animal {
protected String name; // state allowed
Animal(String name) { this.name = name; } // constructor allowed
abstract void sound(); // must be implemented by subclass
void describe() { // concrete method
System.out.println(name + " says");
sound();
}
}
class Dog extends Animal {
Dog(String name) { super(name); }
@Override void sound() { System.out.println("Woof"); }
}
| Rule | Detail |
|---|---|
| Cannot instantiate | new Animal() is illegal. |
| Can have constructors | Invoked via super(). |
| Can have instance fields | Unlike interfaces. |
| Abstract methods have no body | Subclass must implement (or be abstract). |
| Single inheritance | A class extends only one abstract class. |
abstract + final | Illegal. |
9. Interface vs Abstract Class
| Feature | Interface | Abstract Class |
|---|---|---|
| Instantiable | ❌ | ❌ |
| Multiple inheritance | ✅ (implements many) | ❌ (extends one) |
| Instance fields | ❌ (only constants) | ✅ |
| Constructors | ❌ | ✅ |
| Method bodies | default/static/private only | Any (concrete) |
| Field type | public static final only | Any |
| Access modifiers on methods | public (or private) | Any |
| State | Stateless (constants only) | Can hold state |
| Use when | Define a capability/contract | Share code + state |
Rule of thumb: Use an interface for a capability ("can do"); use an abstract class for an is-a relationship with shared state.
10. Records (Java 16+)
A record is a concise, immutable data carrier. The compiler auto-generates the constructor, accessors, equals(), hashCode(), and toString().
record Point(int x, int y) {}
This single line is equivalent to ~30 lines of boilerplate:
Point p = new Point(3, 4);
System.out.println(p.x()); // 3 (accessor is x(), NOT getX())
System.out.println(p.y()); // 4
System.out.println(p); // Point[x=3, y=4]
System.out.println(p.equals(new Point(3, 4))); // true
What the Compiler Generates
| Generated | Detail |
|---|---|
| Private final fields | One per component. |
| Canonical constructor | Takes all components. |
| Accessors | x(), y() — named after components (no get). |
equals() / hashCode() | Based on all components. |
toString() | Point[x=3, y=4] form. |
Compact Constructor (Validation)
record Range(int low, int high) {
Range { // compact canonical constructor
if (low > high)
throw new IllegalArgumentException("low > high");
// fields assigned automatically AFTER this block
}
}
Custom Methods & Static Members
record Circle(double radius) {
static final double PI = 3.14159; // static fields ALLOWED
double area() { return PI * radius * radius; } // extra method OK
Circle { // validation
if (radius < 0) throw new IllegalArgumentException();
}
}
Record Rules (Heavily Tested)
| Rule | Detail |
|---|---|
Implicitly final | A record cannot be extended. |
Extends java.lang.Record | So it cannot extend any other class. |
| Can implement interfaces | ✅ Allowed. |
Components are final | Immutable; no setters. |
| No instance fields beyond components | Only the declared components (static fields OK). |
| Can have static fields/methods | ✅ |
| Can override accessors / equals / etc. | ✅ but rarely needed. |
| Can declare additional constructors | Must delegate to the canonical one via this(...). |
// record cannot extend a class:
// record Bad(int x) extends SomeClass {} // ERROR
record Named(String name) implements Comparable<Named> { // interface OK
public int compareTo(Named o) { return name.compareTo(o.name); }
}
11. Record Patterns (Java 21)
Java 21 lets you deconstruct records directly in instanceof and switch — a flagship feature.
record Point(int x, int y) {}
Object obj = new Point(3, 4);
// instanceof with record pattern
if (obj instanceof Point(int x, int y)) {
System.out.println(x + ", " + y); // 3, 4 — components bound directly
}
Nested Record Patterns
record Point(int x, int y) {}
record Line(Point start, Point end) {}
Object obj = new Line(new Point(0, 0), new Point(5, 5));
if (obj instanceof Line(Point(int x1, int y1), Point(int x2, int y2))) {
System.out.println("From " + x1 + "," + y1 + " to " + x2 + "," + y2);
}
In a switch (with var and guards)
static String describe(Object obj) {
return switch (obj) {
case Point(var x, var y) when x == y -> "diagonal point";
case Point(var x, var y) -> "point " + x + "," + y;
default -> "not a point";
};
}
| Feature | Detail |
|---|---|
| Deconstruction | Extract components into variables in one step. |
| Nesting | Patterns can be nested arbitrarily deep. |
var in patterns | Type inference per component allowed. |
| Guards | Combine with when. |
12. Sealed Classes (Java 17+)
A sealed class/interface restricts which classes may extend or implement it, using the permits clause. This gives you a known, closed set of subtypes.
sealed interface Shape permits Circle, Square, Triangle {}
final class Circle implements Shape {}
final class Square implements Shape {}
non-sealed class Triangle implements Shape {}
The Three Subtype Modifiers (Mandatory)
Every permitted subtype must be exactly one of:
| Modifier | Meaning |
|---|---|
final | No further subclasses. |
sealed | Continues the restriction (needs its own permits). |
non-sealed | Reopens the hierarchy (anyone can extend). |
sealed interface Animal permits Dog, Cat, Wild {}
final class Dog implements Animal {}
sealed class Cat implements Animal permits Kitten {} // sealed continues
non-sealed class Wild implements Animal {} // reopened
final class Kitten extends Cat {}
Sealed Rules (Critical)
| Rule | Detail |
|---|---|
permits lists subtypes | Only listed types may extend/implement. |
| Subtypes must be accessible | In the same module (or same package if unnamed module). |
| Each subtype declares a modifier | final, sealed, or non-sealed. |
permits can be omitted | If all subtypes are in the same source file. |
| Sealed + record | Records are implicitly final, so they fit as permitted subtypes. |
// permits omitted: all subtypes in the same file
sealed interface Expr {}
record Num(int v) implements Expr {} // record is final -> valid
record Add(Expr l, Expr r) implements Expr {}
13. Sealed + Pattern Matching (Java 21)
Sealed types + pattern matching switch enable exhaustive checking — the compiler knows all possible subtypes, so no default is needed.
sealed interface Shape permits Circle, Square, Triangle {}
record Circle(double radius) implements Shape {}
record Square(double side) implements Shape {}
record Triangle(double base, double height) implements Shape {}
static double area(Shape shape) {
return switch (shape) { // NO default needed!
case Circle c -> Math.PI * c.radius() * c.radius();
case Square s -> s.side() * s.side();
case Triangle t -> 0.5 * t.base() * t.height();
};
}
Combined with Record Deconstruction
static double area(Shape shape) {
return switch (shape) {
case Circle(double r) -> Math.PI * r * r;
case Square(double s) -> s * s;
case Triangle(double b, double h) -> 0.5 * b * h;
};
}
Why This Matters
| Benefit | Detail |
|---|---|
| Exhaustiveness | Compiler verifies all subtypes are handled. |
No default | Adding a new permitted subtype causes a compile error until handled. |
| Safe refactoring | The compiler flags every switch that must be updated. |
This is the "algebraic data type" pattern — sealed types + records + pattern matching is the headline modeling feature of modern Java.
14. Certification Traps
| # | Trap |
|---|---|
| 1 | Interface fields are implicitly public static final — no instance fields. |
| 2 | Interface methods are implicitly public abstract (unless default/static/private). |
| 3 | Static interface methods are not inherited — call via interface name. |
| 4 | Diamond conflict on default methods must be resolved by overriding. |
| 5 | "Class wins": a superclass concrete method beats an interface default. |
| 6 | Use InterfaceName.super.method() to call a specific default. |
| 7 | Records are implicitly final and extend Record — cannot extend a class. |
| 8 | Record accessor is x(), not getX(). |
| 9 | Compact constructor has no parameter list and assigns fields implicitly. |
| 10 | Additional record constructors must delegate to the canonical one (this(...)). |
| 11 | Records can implement interfaces and have static fields, but no extra instance fields. |
| 12 | Every sealed subtype must be final, sealed, or non-sealed. |
| 13 | permits can be omitted only if subtypes are in the same file. |
| 14 | Sealed + pattern switch is exhaustive — no default required. |
| 15 | private interface methods must have a body (cannot be abstract). |
15. Common Mistakes
| Mistake | Fix |
|---|---|
| Adding instance fields to an interface | Only constants allowed; use an abstract class. |
| Calling a static interface method via a class | Call via the interface name. |
| Forgetting to resolve a diamond conflict | Override and pick with Interface.super.method(). |
Using getX() on a record | Use the component accessor x(). |
| Trying to extend a record | Records are final; use composition or interfaces. |
| Forgetting a subtype modifier in a sealed hierarchy | Mark each final/sealed/non-sealed. |
Adding default to make a record mutable | Records are immutable by design. |
Adding default keyword and a body that's static | default and static cannot combine. |
16. Interview Questions
Q1. What is the difference between an interface and an abstract class? An interface defines a contract (no state, multiple inheritance, only constants and default/static/private method bodies). An abstract class can hold state, constructors, and concrete methods but supports single inheritance.
Q2. Why were default methods introduced?
To add new methods to existing interfaces without breaking implementing classes (backward compatibility), e.g., Collection.stream().
Q3. Are static interface methods inherited? No — they belong to the interface and must be called using the interface name.
Q4. How do you resolve the diamond problem with default methods?
Override the conflicting method and explicitly call the desired version with InterfaceName.super.method().
Q5. What is the "class wins" rule? If a superclass provides a concrete method and an interface provides a default with the same signature, the superclass method is used.
Q6. What is a record and what does it generate?
An immutable data carrier; the compiler generates final fields, a canonical constructor, accessors, equals(), hashCode(), and toString().
Q7. Can a record extend a class or be extended?
No to both — records implicitly extend Record and are implicitly final. They can implement interfaces.
Q8. What is a compact constructor? A canonical constructor without a parameter list, used for validation/normalization; field assignment happens automatically after the block.
Q9. What is a sealed class?
A class/interface that restricts which types can extend/implement it via the permits clause.
Q10. What modifiers must permitted subtypes use?
Each must be final, sealed, or non-sealed.
Q11. How do sealed types improve switch?
The compiler knows all permitted subtypes, enabling exhaustive switch without a default and flagging unhandled cases at compile time.
Q12. What are record patterns (Java 21)?
A way to deconstruct a record's components directly in instanceof/switch, with support for nesting and guards.
17. Quick Revision Notes
- Interface fields =
public static final; methods =public abstractby default. - Java 8: default + static interface methods; Java 9: private + private static.
- Static interface methods are not inherited; call via interface name.
- Diamond conflict → must override; use
Interface.super.method(). - "Class wins" over interface default.
- Abstract class: state + constructors + concrete methods; single inheritance; can't instantiate.
- Use interface for capability, abstract class for shared state/is-a.
- Records: immutable, implicitly
final, extendRecord, accessorsx()notgetX(). - Compact constructor: no params, validates, auto-assigns fields.
- Records can implement interfaces + have static members, but no extra instance fields.
- Record patterns (21): deconstruct in
instanceof/switch, nestable, supportvar+ guards. - Sealed:
permitslists subtypes; each subtype isfinal/sealed/non-sealed. permitsoptional if subtypes share the file.- Sealed + pattern switch = exhaustive, no
defaultneeded.
18. One-Page Cheat Sheet
======== INTERFACES, ABSTRACT, RECORDS & SEALED CHEAT SHEET ========
INTERFACE
fields: public static final (constants only, no instance state)
methods: public abstract (default)
default void m(){...} // Java 8, instance body, overridable
static void m(){...} // Java 8, NOT inherited, call Iface.m()
private void m(){...} // Java 9, helper for defaults
private static m(){...} // Java 9
class implements many interfaces ; interface extends many interfaces
DIAMOND PROBLEM
two default methods same sig -> class MUST override
pick one: InterfaceName.super.method()
CLASS WINS over interface default
ABSTRACT CLASS
cannot instantiate ; has state + constructors + concrete + abstract
single inheritance ; abstract+final = ILLEGAL
use: shared state/is-a (interface: capability/contract)
RECORD (16+) record Point(int x, int y){}
immutable, implicitly FINAL, extends Record (no class extend)
auto: fields, canonical ctor, x()/y() accessors, equals, hashCode, toString
accessor is x() NOT getX()
compact ctor: Point { if(...) throw ...; } // no params, auto-assign
can: implement interfaces, static fields/methods, extra methods
extra constructors must delegate this(...)
RECORD PATTERNS (21)
if (o instanceof Point(int x, int y)) {...}
nested: instanceof Line(Point(int x1,int y1), Point(int x2,int y2))
switch: case Point(var x, var y) when x==y -> ...
SEALED (17+)
sealed interface Shape permits Circle, Square {}
each subtype: final | sealed (own permits) | non-sealed
permits optional if all subtypes in SAME FILE
records fit (they are final)
SEALED + SWITCH (21)
switch(shape){ case Circle c->...; case Square s->...; } // NO default
exhaustive: compiler verifies all permitted subtypes handled
====================================================================
End of 06 - Interfaces, Abstract Classes, Records, and Sealed Classes.