W3docs

Java Abstraction

Hide implementation details behind abstract types in Java using abstract classes and interfaces.

Abstraction is the fourth pillar of OOP: describe what something does without committing to how. You declare the operations a type supports, leave the implementation to the concrete classes, and write the rest of your program against the abstract type. This chapter is the conceptual overview — Java's two mechanisms for it, abstract class and interface, each get their own dedicated chapter.

The two questions

Every type declaration in Java answers two questions:

  1. What can callers do with values of this type? (its API)
  2. How is each of those operations implemented? (its body)

A concrete class answers both. An abstract type answers only the first and leaves the second to subtypes:

public interface Shape {
  double area();         // what — every Shape has an area
}

public class Circle implements Shape {
  double r;
  public Circle(double r) { this.r = r; }
  public double area() { return Math.PI * r * r; }   // how
}
public class Square implements Shape {
  double side;
  public Square(double side) { this.side = side; }
  public double area() { return side * side; }
}

Shape says "every shape has an area." Circle and Square say how to compute one. Code that takes a Shape doesn't care which:

double sumAreas(List<Shape> shapes) {
  double sum = 0;
  for (Shape s : shapes) sum += s.area();
  return sum;
}

This function is closed over the abstraction. It works for Circle and Square today; for Triangle tomorrow; for Polygon six months from now. None of the new types require any change to sumAreas.

Java's two mechanisms

MechanismWhat it providesWhen to reach for it
abstract classA partial class — some methods abstract, others with bodies, plus fields and constructorsWhen subtypes will share state and infrastructure code
interfaceA pure (or near-pure) contract — methods that implementing classes must provide; no instance stateWhen subtypes only need to agree on a set of operations and may have nothing else in common

A class extends one abstract class. A class can implement many interfaces. That asymmetry steers a lot of designs: if you find yourself wanting "multiple inheritance," interfaces are usually the answer.

Abstract classes — partial implementation

abstract on a class means "you can't instantiate this directly — only subclasses." abstract on a method means "no body here; every concrete subclass must provide one":

public abstract class Shape {
  public abstract double area();      // every Shape must define this

  // a concrete method, shared across all shapes
  public final String describe() {
    return getClass().getSimpleName() + " area=" + area();
  }
}

new Shape() is a compile error. new Circle() works. Inside describe, the call area() dispatches to the actual subclass's implementation — same polymorphism mechanism as any overridden method.

Use an abstract class when subtypes really do share code. If you find yourself writing the same helper in three subclasses, that's a signal to lift it into the parent.

Interfaces — the contract

An interface declares operations and leaves the implementation entirely to whoever implements it:

public interface Comparable<T> {
  int compareTo(T other);
}

public class Money implements Comparable<Money> {
  private final long cents;
  public int compareTo(Money other) {
    return Long.compare(this.cents, other.cents);
  }
}

Now Money works anywhere a Comparable is expected — Collections.sort(...), TreeMap, Arrays.sort(...), your own generic algorithms. The standard library and your code agree on Comparable as a shared abstraction; neither side knows about the other.

The vast majority of Java's standard interfaces (List, Map, Iterable, Runnable, Function, Comparator, AutoCloseable) work this way: a small, focused contract that many concrete classes plug into.

Abstraction as a design lever

The mechanical part of abstraction — the abstract keyword, the interface declaration — is small. The hard part is choosing which abstractions to define. Three patterns that show up over and over:

  • Strategy. Define an interface for "the algorithm." Different implementations swap out the algorithm without changing the code that uses it. Comparator is the classic.
  • Template method. An abstract class implements the overall flow, with abstract methods at the variation points. Subclasses fill in the specific steps. HttpServlet's service method is a famous example.
  • Plugin / extension point. A library publishes an interface; user code implements it; the library calls back into it. Servlet APIs, JDBC drivers, Spring's BeanPostProcessor.

In every case, the win is the same: code that depends on the abstraction is closed against changes to the implementations, and open to additional implementations being added later.

Encapsulation vs abstraction

These two are close cousins and often get confused.

  • Encapsulation hides the implementation of one specific class (private fields, controlled methods). It's a class-internal concern.
  • Abstraction hides which class you're talking to behind a shared contract. It's a class-external concern.

A class with private fields and a tidy public API is encapsulated, but it's not yet abstracted — callers still depend on that specific class. Replace the type at the API boundary with an interface, and callers depend on the contract instead. Now you can swap implementations.

A worked example

java— editable, runs on the server

What's next

The next chapter is the concrete mechanics of abstract classes — abstract methods, what they let a subclass inherit, when to pick them over interfaces.

Practice

Practice

Which best captures the difference between encapsulation and abstraction?