Interfaces & Abstraction Best Practices

Abstraction is how you hide messy detail behind a clean contract. Learn to design with abstract classes and interfaces so your code stays flexible, swappable, and easy to extend.

Learn Interfaces & Abstraction Best Practices in our free Java course — a beginner-friendly interactive lesson with worked examples, a practice exercise and…

Part of the free Java course at LearnCodingFast — hands-on lessons with examples you run in your browser, plus practice exercises and a quick quiz.

This lesson focuses on design . You should already know the basic syntax of:

The earlier Interfaces lesson taught the syntax. This one teaches you how to design with abstraction so your programs stay easy to change.

A wall socket is an abstraction . It promises one thing: "plug in here and you get power." It says nothing about how that power was made — coal, solar, wind, or a nuclear plant. Your kettle does not know and does not care.

That is the whole point. Because every appliance depends only on the socket contract , the power company can swap a coal plant for a wind farm and not a single kettle breaks. In code, the socket is your interface or abstract class ; the power plants are the concrete classes that implement it.

The principle: depend on the stable contract (the socket), not the volatile detail (the power plant). That is the abstraction principle, and it is what keeps large programs from becoming impossible to change.

An abstract class is a class marked with the abstract keyword that is deliberately incomplete . It describes a general idea — a Payment , a Shape , an Animal — that is too vague to exist on its own. You can never write new Payment(...) ; the compiler stops you.

It becomes useful through abstract methods : method signatures with no body, ending in a semicolon. An abstract method is a promise: "every concrete subclass must supply this." The abstract class can also hold concrete methods (real, shared code) and fields, so common behaviour is written once.

💡 Why this matters: the abstract class captures everything the subtypes have in common, and the abstract methods mark exactly the parts that differ. You stop copy-pasting shared code, and the compiler forces every subclass to fill in the gaps.

In the example below, receipt() is shared by every payment, while authorize() is the one detail each payment type must define itself.

The whole vocabulary of abstraction comes down to one split: which parts are fixed and shared , and which parts vary . Get this distinction clear and the rest follows.

The single most useful design rule in object-oriented code is: "program to an interface, not an implementation." In practice it means your variables, parameters, and return types use the abstract type , and the concrete class name appears only at the one spot where you call new .

Because the method below takes a Notifier rather than an EmailNotifier , you can hand it any notifier — present or future — without changing a line. That flexibility is the payoff of designing to the abstraction.

🔁 Polymorphism through abstraction: one call — n.send(message) — does something different for each implementation. The calling code stays the same; the behaviour changes. That is polymorphism, and abstraction is what makes it possible.

Fill in the two blanks so Circle honours the Shape interface. You declare that it follows the contract, then supply the body the contract demands.

Hint: a class promises to follow an interface with the implements keyword, and a circle's area is Math.PI * radius * radius .

Sometimes the order of steps is what you want to reuse, while the steps themselves vary. The template method pattern captures exactly this: an abstract class writes one concrete method that lays out the algorithm's skeleton, calling abstract steps that subclasses fill in.

Mark the skeleton method final so subclasses can override the steps but never reorder the algorithm . A step can also have a sensible default (here, footer() ) that subclasses may override only if they need to.

Notice the win: generate() is written once and guarantees every report has a header, rows, then a footer — in that order. A new report type only fills in header() and rows() .

Make Animal an abstract class with an abstract sound() method, so the shared describe() method can call it. Dog supplies the concrete detail.

Hint: the keyword that marks both the class and the method is the same word — abstract — and the abstract method ends with a semicolon, no braces.

Both are tools for abstraction, and beginners agonise over which to use. Here is the honest comparison, followed by a rule that resolves most cases.

🧭 Decision rule: if subtypes share state or constructor logic , or you want a template method , reach for an abstract class. If you just need a contract of capabilities — especially one a class might mix with others — use an interface. When genuinely in doubt, prefer the interface: it keeps your options open because a class can implement many.

Now write it yourself from an outline only — no filled-in logic this time. Design a tiny Discount abstraction with two interchangeable implementations, and a checkout method that depends only on the abstraction.

If checkout mentions PercentOff or FlatOff by name, you have coupled to an implementation — change its parameter type back to Discount .

💡 Name abstractions by role, not by implementation: Notifier , Repository , Discount — never EmailNotifierInterface .

💡 Keep contracts small. A focused interface with two methods is easier to implement and reuse than one with twenty.

💡 Abstract is a design choice, not decoration. Introduce it when you have a real reason to vary something — a second implementation, a test double, a swappable provider.

You can now design with abstraction, not just use it. You know how abstract classes and methods carve the shared from the varying, why an abstract class can't be instantiated, how to program to an interface so implementations stay swappable, and how the template method pattern and polymorphism fall out of good abstraction — plus how to choose between an abstract class and an interface.

Next: Inner Classes & Anonymous Classes — static, non-static, local, and anonymous inner classes, including the quick one-off implementations of the interfaces you just designed.

Practice quiz

What happens when you write new Payment(10) where Payment is an abstract class?

  • It runs normally
  • It returns null
  • Compile error: Payment is abstract; cannot be instantiated
  • It throws at runtime

Answer: Compile error: Payment is abstract; cannot be instantiated. You can never instantiate an abstract class. Create a concrete subclass like new CardPayment(10) instead.

What is an abstract method?

  • A method signature with no body, ending in a semicolon
  • A method with a body that cannot be overridden
  • A static method on an interface
  • A private helper method

Answer: A method signature with no body, ending in a semicolon. An abstract method has no body — just a signature ending in ';' — and every concrete subclass must implement it.

Which keyword makes a class promise to follow an interface's contract?

  • extends
  • inherits
  • uses
  • implements

Answer: implements. A class uses 'implements' to follow an interface, and must define every method the interface declares.

What does 'program to an interface, not an implementation' mean?

  • Never use concrete classes
  • Use the abstract type for variables/params, naming the concrete class only at 'new'
  • Always use abstract classes over interfaces
  • Put all logic in the interface

Answer: Use the abstract type for variables/params, naming the concrete class only at 'new'. Declare variables, parameters, and return types with the abstract type so implementations stay swappable.

How many classes can a Java class extend, and how many interfaces can it implement?

  • One class, many interfaces
  • One class, one interface
  • Many classes, one interface
  • Many classes, many interfaces

Answer: One class, many interfaces. A class extends only one class but can implement many interfaces — a key reason to favour interfaces when in doubt.

In the template method pattern, why is the skeleton method often marked final?

  • So it runs faster
  • So it can be static
  • So subclasses cannot reorder or change the algorithm's steps
  • So it returns void

Answer: So subclasses cannot reorder or change the algorithm's steps. final locks the algorithm: subclasses fill in the abstract steps but can't change the fixed order of the template.

When is a subclass of an abstract class itself concrete (instantiable)?

  • Always
  • Only when every inherited abstract method has a body
  • Only when it adds new fields
  • Never

Answer: Only when every inherited abstract method has a body. A class is concrete only when every abstract method it inherits is implemented; otherwise it must be marked abstract too.

For a Circle implementing Shape, what is the correct body of area()?

  • return radius * radius;
  • return 2 * Math.PI * radius;
  • return Math.PI * radius;
  • return Math.PI * radius * radius;

Answer: return Math.PI * radius * radius;. A circle's area is Math.PI * radius * radius (pi r squared). For radius 2 that prints 12.57.

When should you prefer an abstract class over an interface?

  • When you only need a contract of capabilities
  • When subtypes share state or constructor logic
  • When a class must mix many types
  • Never — interfaces are always better

Answer: When subtypes share state or constructor logic. Abstract classes can hold fields, constructors, and shared method bodies — ideal when subtypes share state or a template.

What enables one call like n.send(message) to behave differently per implementation?

  • Encapsulation
  • Static binding
  • Polymorphism through abstraction
  • Generics

Answer: Polymorphism through abstraction. Calling a method on an abstract type dispatches to each concrete implementation — that is polymorphism via abstraction.