Abstract classes and interfaces
What each is for, the full comparison, default and static interface methods, functional interfaces, and how to answer which one you would use.
"Abstract class or interface?" is asked in almost every Java round, and the answer most students give is the comparison table. Learn the table, then learn the sentence that makes it useful.
Abstract classes
An abstract class cannot be instantiated. It exists to be extended, and it may leave some methods unimplemented:
abstract class Assessment {
protected final String title;
protected int durationMinutes;
Assessment(String title, int durationMinutes) { // abstract classes CAN have constructors
this.title = title;
this.durationMinutes = durationMinutes;
}
abstract int totalMarks(); // no body — subclasses must provide one
// a concrete method — shared behaviour, written once
String summary() {
return title + ": " + totalMarks() + " marks in " + durationMinutes + " minutes";
}
}class Aptitude extends Assessment {
private final int questions;
Aptitude(int questions) {
super("Aptitude", 60);
this.questions = questions;
}
@Override
int totalMarks() { return questions; }
}
System.out.println(new Aptitude(40).summary());
// Aptitude: 40 marks in 60 minutes
new Assessment("x", 10); // compile error: Assessment is abstractFacts about abstract classes that get asked:
- An abstract class need not contain any abstract method. Marking it
abstractsimply forbids instantiation. - A class with even one abstract method must be declared abstract.
- It can have constructors, fields, static members and concrete methods.
- A subclass must implement every inherited abstract method, or itself be abstract.
abstractandfinaltogether are contradictory and will not compile. Nor willabstractwithprivateorstatic.
Interfaces
An interface is a contract: a set of method signatures a class promises to provide.
interface Gradable {
int PASS_MARK = 40; // implicitly public static final
int score(); // implicitly public abstract
default boolean passed() { // Java 8+ — a body, inherited by implementers
return score() >= PASS_MARK;
}
static String describe(Gradable g) { // Java 8+ — belongs to the interface itself
return g.passed() ? "cleared" : "not cleared";
}
}class Paper implements Gradable {
private final int marks;
Paper(int marks) { this.marks = marks; }
@Override public int score() { return marks; }
}
Paper p = new Paper(55);
System.out.println(p.passed()); // true — the default method
System.out.println(Gradable.describe(p)); // cleared — the static methodWhat is implicit in an interface, and therefore redundant to write:
- Fields are
public static final— always constants, never state. - Methods with no body are
public abstract. - You cannot have instance fields, and you cannot have a constructor. An interface has no state to initialise.
Since Java 8 an interface can also have default and static methods, and since Java 9 private
methods to share code between defaults. default was added specifically so that a new method could
be introduced into a widely-implemented interface without breaking every implementer — which is
exactly how Iterable.forEach and Collection.stream were added.
A class can implement many interfaces
This is the practical difference:
interface Timed { int durationMinutes(); }
interface Proctored { boolean cameraRequired(); }
interface Gradable { int score(); }
class MockTest implements Timed, Proctored, Gradable {
@Override public int durationMinutes() { return 180; }
@Override public boolean cameraRequired() { return true; }
@Override public int score() { return 72; }
}Three unrelated capabilities on one class. With abstract classes you would have to pick one and awkwardly fold the others in.
When two interfaces collide
If two interfaces give the same default method, the class must resolve it explicitly — this is the one place Java makes you choose:
interface A { default String hello() { return "A"; } }
interface B { default String hello() { return "B"; } }
class C implements A, B {
@Override
public String hello() {
return A.super.hello(); // pick one, or write something new
}
}A.super.hello() is the syntax. Without the override, class C implements A, B will not compile.
The comparison table
| Abstract class | Interface | |
|---|---|---|
| Instance fields (state) | yes | no |
| Constructor | yes | no |
| Method bodies | yes | default and static only |
| Constants | yes | yes, implicitly public static final |
| Access modifiers on members | any | public (plus private helpers) |
| How many can a class have? | one | many |
| Extends | one class | many interfaces |
| Purpose | share implementation among related types | declare a capability |
The sentence that makes it useful
An abstract class says what something is. An interface says what something can do.
Use an abstract class when the subclasses are genuinely variations of one thing and there is
real code to share — a base Assessment with common fields and a working summary().
Use an interface when unrelated classes need the same capability, or when you want callers to
depend on a contract rather than a class. Comparable, Runnable and Serializable are all
capabilities, and the classes implementing them have nothing else in common.
If asked to choose and both fit: prefer the interface. A class can implement several, so it leaves the design open; extending a class spends the one inheritance slot you have.
And they combine well, which is the pattern in the standard library itself:
interface Round { int marks(); }
abstract class TimedRound implements Round { // shared plumbing
protected final int minutes;
TimedRound(int minutes) { this.minutes = minutes; }
public int minutes() { return minutes; }
}
class Aptitude extends TimedRound {
Aptitude() { super(60); }
@Override public int marks() { return 40; }
}List is an interface; AbstractList is an abstract class implementing the boring half; ArrayList
extends it. That is the shape to describe if you are asked for an example.
Functional interfaces
An interface with exactly one abstract method is a functional interface, and it can be implemented with a lambda:
@FunctionalInterface
interface Marker {
int mark(String answer); // exactly one abstract method
}
Marker exact = answer -> answer.equals("42") ? 1 : 0;
System.out.println(exact.mark("42")); // 1@FunctionalInterface is optional but makes the compiler enforce the single-method rule. default
and static methods do not count against it.
The standard library provides the common shapes in java.util.function, so you rarely declare your
own:
Predicate<String> isEmpty = s -> s.isEmpty(); // T → boolean
Function<String, Integer> len = s -> s.length(); // T → R
Consumer<String> print = s -> System.out.println(s);// T → void
Supplier<String> now = () -> "value"; // () → T
BiFunction<Integer, Integer, Integer> add = (a, b) -> a + b;
Comparator<String> byLength = (a, b) -> a.length() - b.length();This is the bridge into lesson 12: Comparator and Runnable are functional interfaces, which is
why sorting and threading both read as one-liners in modern Java.
Anonymous classes
Before lambdas, a one-off implementation was written inline as an anonymous class. You will still meet it, and for an interface with more than one method it is still the only option:
Runnable oldStyle = new Runnable() {
@Override public void run() { System.out.println("running"); }
};
Runnable newStyle = () -> System.out.println("running");The two are not identical underneath — a lambda does not create a new class file, and this inside
a lambda refers to the enclosing instance rather than to the anonymous object. That distinction is
a good answer if the interview goes deeper.
What to take into an interview
- An abstract class cannot be instantiated; it may mix abstract and concrete methods, and it can have state and constructors.
- An interface declares a capability: no instance state, no constructor,
defaultandstaticbodies allowed since Java 8. - One superclass, many interfaces. That is usually the deciding factor.
- Conflicting
defaultmethods must be resolved withA.super.method(). - Abstract class = "is a"; interface = "can do". When both fit, prefer the interface.
- An interface with one abstract method is a functional interface and can be written as a lambda.