Lesson 07 · 10 min read · Java for placements

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 abstract

Facts about abstract classes that get asked:

  • An abstract class need not contain any abstract method. Marking it abstract simply 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.
  • abstract and final together are contradictory and will not compile. Nor will abstract with private or static.

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 method

What 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, default and static bodies allowed since Java 8.
  • One superclass, many interfaces. That is usually the deciding factor.
  • Conflicting default methods must be resolved with A.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.
Lesson 7 of 12 · all 12 are free to read, no account. Course contents