Interfaces & Abstract Classes

Course 1 · Ch 6
Interfaces & Abstract Classes
Java's own third answer to the diamond problem — genuinely comparable to both C++'s fix and Rust's structural avoidance

java1-5 closed with a promise: state can only ever come from one superclass, but behavior can still come from multiple sources — through interfaces. This chapter delivers on that.

Interfaces — The Contract

public interface Movable { void move(double distance); // signature only — no body, no state } public class Robot implements Movable { @Override public void move(double distance) { System.out.println("Rolling " + distance + " units"); } }

An interface declares method signatures with no body and no instance fields — a pure contract. A class implements it and must supply real bodies for every abstract method it declares.

A Class Can implements Many Interfaces

public class Robot implements Movable, Chargeable, Loggable { // no cap, unlike extends // ... }

This is exactly where "multiple sources of behavior" comes back from java1-5. extends is capped at one superclass; implements has no such cap at all — a single class can satisfy as many interface contracts as it genuinely needs to.

Default Methods (Java 8+)

public interface Movable { void move(double distance); default void stop() { // a real method body, right inside the interface System.out.println("Stopping."); } }

Since Java 8, an interface method can carry a default body — implementing classes inherit it automatically and may override it if they need different behavior. This is a genuine convergence point with Rust: rust2-2's own trait default methods work the same way, behavior supplied by the contract itself without requiring every implementer to repeat it.

Abstract Classes

public abstract class Shape { protected String name; // abstract classes CAN hold state public Shape(String name) { this.name = name; } // and constructors public abstract double area(); // no body — subclasses must supply one public String describe() { // a real, shared, concrete method return name + " has area " + area(); } }

Unlike an interface, an abstract class can hold real fields, constructors, and fully concrete methods alongside abstract ones. It still follows java1-5's own single-inheritance rule — a class extends at most one abstract class, exactly the same as any other class.

Interface vs. Abstract Class — When to Use Which

An interface answers "can this do X?" with no shared state at all. An abstract class answers "is this genuinely a kind of Y?" and gets to share real implementation and state across every subclass. A class can implement many interfaces but extend only one abstract class — the choice usually follows from that asymmetry directly.

The Diamond Problem, Returning For Behavior Alone

Two interfaces can both supply conflicting default implementations of the same method — a real, narrow reappearance of cpp3-1's diamond problem, scoped to behavior only, since interfaces never carry state. Java doesn't guess which one wins: the implementing class is required to override the method itself and resolve the conflict explicitly, or the code simply fails to compile.

MechanismC++ (cpp3-1)Rust (rust2-2)Java
Multiple inheritance of stateallowed — causes the diamond problemnever possible — structs don't inheritnever possible — single extends only
Multiple sources of behaviormultiple inheritance (same mechanism as state)traits, no state carriedinterfaces, no state carried
Conflicting default behaviorvirtual inheritance resolves it structurallycompile error — must be resolved explicitlycompile error — must be overridden explicitly
Default to interfaces when there's no shared state
Since a class can implement many interfaces but extend only one class, prefer an interface whenever the contract genuinely needs no shared fields or constructor logic — it keeps every other inheritance slot free for something that does.
Two conflicting default methods won't compile silently
If a class implements two interfaces that both provide a default method with the same signature, Java refuses to guess which one applies — the class must override that method itself, even if only to explicitly pick one interface's version via InterfaceName.super.methodName().

Coding Challenges

Challenge 1

Write an interface Printable with an abstract method print() and a default method printTwice() that calls print() twice, then a class implementing Printable that only supplies print().

📄 View solution
Challenge 2

Write an abstract class Employee with a protected name field, a constructor, an abstract method pay(), and a concrete method summary() that calls pay(). Then write a Manager subclass that supplies pay().

📄 View solution
Challenge 3

Write two interfaces that each declare a default method with the identical signature but different bodies, then a class that implements both. Show the resulting compile error, then fix it by overriding the method explicitly.

📄 View solution

Chapter 6 Quick Reference

  • Interfaces declare method contracts with no state; a class can implements as many as it needs, unlike extends's single-superclass cap
  • default methods (Java 8+) give an interface a real method body — a genuine convergence point with Rust's own trait default methods
  • Abstract classes can hold state, constructors, and concrete methods alongside abstract ones — but still follow single inheritance
  • Interface = "can do," no shared state; abstract class = "is a," shared state and implementation
  • Conflicting default methods from two interfaces force an explicit override — Java never silently picks a winner
  • Next chapter: exception handling — checked vs. unchecked exceptions