Your country

Tools that support it use your country for local currency, number formats, units and paper size. Your choice is saved only in this browser.

Type a name or a two-letter code. Use the up and down arrow keys to move through the countries, Enter to choose one and Escape to close.

Java  Module 6 – Inheritance, interfaces and polymorphism

Abstract classes and template methods

Declare Java abstract classes and methods, write a final template method that subclasses fill in, and choose between an abstract class and an interface.

  • Intermediate
  • 20 minutes
  • Examples run with Java 25.0.4 and Python 3.14.8
  • By MySmartCoPilot
Free lesson, Pro answers: 20 minutes
Free: the whole lesson. With a pass: the model answers to its 2 interview questions.

What you will learn

  • Declare abstract classes and abstract methods
  • Implement the template method pattern with a final skeleton method
  • Use a JDK abstract class such as AbstractList by implementing only its abstract methods
  • Choose between an abstract class and an interface

Before you start

What you’ll learn · Before you start

What you will learn

  • Declare abstract classes and abstract methods
  • Implement the template method pattern with a final skeleton method
  • Use a JDK abstract class such as AbstractList by implementing only its abstract methods
  • Choose between an abstract class and an interface

Before you start

On this page

An abstract class is a class that is unfinished on purpose. You declare it with abstract class, nobody can create an object of it with new, and it may declare abstract methods, methods with a signature but no body, which every concrete subclass must implement. In every other way it is an ordinary class: it can hold fields, constructors that check them, and finished methods. That mix makes it the natural home of the template method, a design in which one final method of the superclass fixes the steps of a job and their order, and calls abstract methods for the steps that differ, so each subclass writes only those. An interface, the subject of the next lesson, can also declare methods without bodies, but it cannot hold the state of an object, and that difference usually decides between the two.

This lesson builds one template, shows seven declarations that the compiler refuses, and gets a complete List out of the JDK’s AbstractList with two short methods.

An abstract report and two concrete ones

Reports.java Java
// Reports.java: one final render() method, and two kinds of report that supply its steps.
import java.util.List;

class Reports {
    static void main() {
        List<Sale> sales = List.of(
                new Sale("masala dosa", 3, 27_000),
                new Sale("filter coffee", 5, 15_000),
                new Sale("idli", 4, 16_000));
        Report text = new TextReport("Counter sales", sales);
        Report csv = new CsvReport("Counter sales", sales);
        IO.print(text.render());
        IO.println("---");
        IO.print(csv.render());
        try {
            new CsvReport(" ", sales);
        } catch (IllegalArgumentException e) {
            IO.println("---\nrefused: " + e.getMessage());
        }
    }
}

record Sale(String item, int quantity, long amountPaise) { }

abstract class Report {
    private final String title;
    private final List<Sale> sales;

    protected Report(String title, List<Sale> sales) {
        if (title == null || title.isBlank()) {
            throw new IllegalArgumentException("a report needs a title");
        }
        if (sales.isEmpty()) {
            throw new IllegalArgumentException("a report needs at least one sale");
        }
        this.title = title;
        this.sales = List.copyOf(sales);
    }

    /** The template method: the steps and their order are fixed here, once. */
    final String render() {
        StringBuilder out = new StringBuilder(header(title));
        long total = 0;
        for (Sale sale : sales) {
            out.append(row(sale));
            total += sale.amountPaise();
        }
        if (showsTotal()) {
            out.append(footer(total));
        }
        return out.toString();
    }

    protected abstract String header(String title);

    protected abstract String row(Sale sale);

    protected abstract String footer(long totalPaise);

    /** A hook: a step with a default that subclasses may override. */
    protected boolean showsTotal() { return true; }

    static String rupees(long paise) {
        return String.format("%d.%02d", paise / 100, paise % 100);
    }
}

class TextReport extends Report {
    TextReport(String title, List<Sale> sales) { super(title, sales); }

    @Override
    protected String header(String title) { return title + "\n" + "=".repeat(title.length()) + "\n"; }

    @Override
    protected String row(Sale s) { return String.format("%-14s %2d %8s\n", s.item(), s.quantity(), rupees(s.amountPaise())); }

    @Override
    protected String footer(long totalPaise) { return String.format("%-17s %8s\n", "total", rupees(totalPaise)); }
}

class CsvReport extends Report {
    CsvReport(String title, List<Sale> sales) { super(title, sales); }

    @Override
    protected String header(String title) { return "item,quantity,amount\n"; }

    @Override
    protected String row(Sale s) { return s.item() + "," + s.quantity() + "," + rupees(s.amountPaise()) + "\n"; }

    @Override
    protected String footer(long totalPaise) { return ""; }

    @Override
    protected boolean showsTotal() { return false; }       // a CSV file holds the rows and nothing else
}
Reports.java, run with JDK 25 Python · reports_session.py
from session import run, version

version()
run("""
java Reports.java
""")

Output

# openjdk version "25.0.4.1" LTS
$ java Reports.java
Counter sales
=============
masala dosa     3   270.00
filter coffee   5   150.00
idli            4   160.00
total               580.00
---
item,quantity,amount
masala dosa,3,270.00
filter coffee,5,150.00
idli,4,160.00
---
refused: a report needs a title

Recorded with Python 3.14.8 on macOS 26 arm64. To run it yourself: mise exec python@3.14.8 -- python3 reports_session.py

Report is abstract, yet most of the program’s logic lives in it:

  • State and its rules. Report keeps the title and the sales, and its constructor refuses a blank title or an empty list. Every subclass gets those checks without repeating them: new CsvReport(" ", sales) fails in Report’s constructor, which CsvReport’s super(title, sales) runs. The constructor is protected, a common choice, because only subclasses ever call it.
  • The fixed part of the job. render() writes the header, one row for each sale, and the footer with the total. It is final, so no subclass can change that order.
  • The parts that differ. header(), row() and footer() are abstract. TextReport and CsvReport implement all three, and render() reaches their versions through dynamic dispatch, as in the previous lesson.

Report text = new TextReport(...) is ordinary code. An abstract class cannot have objects of its own, but its type works like any other: a Report variable can refer to an object of any concrete subclass.

What the compiler checks

AbstractErrors.java and FinalTemplate.java

Java (AbstractErrors.java) · AbstractErrors.java

// AbstractErrors.java: six abstract declarations that the compiler refuses.
class AbstractErrors {
    static void main() {
        Report r = new Report();                           // an abstract class has no objects of its own
    }
}

abstract class Report {
    abstract String body();
}

class PlainReport extends Report { }                       // body() is still missing

abstract final class LockedReport { }                     // final forbids the subclasses abstract needs

abstract class DraftReport {
    abstract String body() { return ""; }                  // an abstract method has no body
    private abstract String footer();                      // a private method cannot be overridden
    static abstract String title();                        // a static method cannot be overridden either
}

Java (FinalTemplate.java) · FinalTemplate.java

// FinalTemplate.java: a subclass that tries to replace the template method itself.
class FinalTemplate {
    static void main() { }
}

abstract class Report {
    final String render() { return header() + body(); }

    protected abstract String header();

    protected abstract String body();
}

class QuickReport extends Report {
    @Override
    String render() { return body(); }                     // would skip the header

    @Override
    protected String header() { return "Quick report\n"; }

    @Override
    protected String body() { return "all good\n"; }
}
Seven declarations the compiler refuses Python · errors_session.py
from session import run, version

version()
run("""
!java AbstractErrors.java
!java FinalTemplate.java
""")

Output

# openjdk version "25.0.4.1" LTS
$ java AbstractErrors.java
AbstractErrors.java:14: error: illegal combination of modifiers: abstract and final
abstract final class LockedReport { }                     // final forbids the subclasses abstract needs
               ^
AbstractErrors.java:18: error: illegal combination of modifiers: abstract and private
    private abstract String footer();                      // a private method cannot be overridden
                            ^
AbstractErrors.java:19: error: illegal combination of modifiers: abstract and static
    static abstract String title();                        // a static method cannot be overridden either
                           ^
AbstractErrors.java:4: error: Report is abstract; cannot be instantiated
        Report r = new Report();                           // an abstract class has no objects of its own
                   ^
AbstractErrors.java:12: error: PlainReport is not abstract and does not override abstract method body() in Report
class PlainReport extends Report { }                       // body() is still missing
^
AbstractErrors.java:17: error: abstract methods cannot have a body
    abstract String body() { return ""; }                  // an abstract method has no body
                    ^
6 errors
error: compilation failed
[exit status 1]
$ java FinalTemplate.java
FinalTemplate.java:16: error: render() in QuickReport cannot override render() in Report
    String render() { return body(); }                     // would skip the header
           ^
  overridden method is final
1 error
error: compilation failed
[exit status 1]

Recorded with Python 3.14.8 on macOS 26 arm64. To run it yourself: mise exec python@3.14.8 -- python3 errors_session.py

  • No objects of an abstract class. new Report() fails with “Report is abstract; cannot be instantiated”.
  • Every abstract method gets a body somewhere. PlainReport inherits body() without implementing it, and the message says what to do: implement body(), or declare PlainReport abstract too and leave the job to its own subclasses.
  • Abstract methods have no body, and they live only in abstract classes. A class with even one abstract method, declared or inherited and not implemented, must be declared abstract. The reverse is not required: an abstract class may have no abstract methods at all, just to prevent new.
  • abstract contradicts three modifiers. It needs subclasses that override, so it cannot be combined with final, which forbids subclasses, nor with private or static, because neither kind of method can be overridden.
  • A final template cannot be replaced. QuickReport tries to override render() to skip the header, and the compiler refuses: “overridden method is final”.

The template method, step by step

Report, abstract, owns the final render() and declares header, row, footer and a showsTotal hook; TextReport and CsvReport extend it.Report, abstract final render()calls the steps abstract header(),row(), footer() hook showsTotal()TextReport header(): titlerow(): columnsfooter(): totalCsvReport header(): namesrow(): CSV linefooter(): emptyshowsTotal(): falseextendsextends

The report template and its two subclasses

Text description of the diagram

The diagram shows three classes. Report, at the top, is abstract. It declares the final method render(), which calls the steps of a report in a fixed order, the abstract methods header(), row() and footer(), which have no body, and the hook showsTotal(), which returns true unless a subclass overrides it.

Two classes extend Report, shown below it with arrows labelled extends:

  • TextReport writes the title with an underline as its header, each sale as a row of aligned columns, and a total line as its footer.
  • CsvReport writes the column names as its header, each sale as comma-separated values, and an empty footer, and it overrides showsTotal() to return false, so render() leaves the total out.

render() is the template method: it holds the algorithm, and its steps are methods that subclasses fill in. It calls four of them, and each has one of three roles:

  • header(title), abstract. TextReport writes the title, underlined; CsvReport writes the column names.
  • row(sale), abstract, called once for each sale. TextReport writes aligned columns; CsvReport writes comma-separated values.
  • showsTotal(), a hook that returns true. TextReport inherits it; CsvReport overrides it to return false.
  • footer(total), abstract. TextReport writes the total line; CsvReport returns an empty string, which render() never asks for, because the hook said no.

A hook is a step with a default implementation that subclasses may override and usually do not. CsvReport uses one to leave the total out, because a CSV file should hold nothing but its rows, and render() never has to know which subclass it is serving. Hooks are also the safe way to grow a template: a new hook with a default leaves every existing subclass compiling, while a new abstract method breaks each one until it implements the method.

Three habits keep a template sound:

  • Make the template method final, so the order of the steps and the work between them, such as adding up the total, stays in one place.
  • Make the steps protected: subclasses implement them, and callers use the template. The steps of Report are meant to be called by render(), not by the code that prints a report.
  • Keep the steps small and give each one a single job. A step that does several things forces every subclass to repeat the parts it did not want to change.

The same design in the JDK

The JDK uses templates in many of its abstract classes. java.util.AbstractList implements the whole List interface on top of two abstract methods, get(int) and size():

Multiples.java Java
// Multiples.java: a whole List<Integer> from AbstractList, with only get() and size() written.
import java.util.AbstractList;
import java.util.List;

class Multiples extends AbstractList<Integer> {
    private final int step;
    private final int count;

    Multiples(int step, int count) {
        if (step <= 0 || count < 0) {
            throw new IllegalArgumentException("step must be positive and count must not be negative");
        }
        this.step = step;
        this.count = count;
    }

    @Override
    public Integer get(int index) {
        if (index < 0 || index >= count) {
            throw new IndexOutOfBoundsException("index " + index + ", size " + count);
        }
        return step * (index + 1);
    }

    @Override
    public int size() { return count; }

    static void main() {
        List<Integer> sevens = new Multiples(7, 10);       // no array: each value is computed by get()
        IO.println(sevens);
        IO.println("contains 42: " + sevens.contains(42) + ", indexOf(42): " + sevens.indexOf(42));
        IO.println("subList(2, 5): " + sevens.subList(2, 5));
        int sum = 0;
        for (int n : sevens) {
            sum += n;
        }
        IO.println("sum: " + sum);
        IO.println("equals List.of(7, 14): " + new Multiples(7, 2).equals(List.of(7, 14)));
        try {
            sevens.set(0, 1);
        } catch (UnsupportedOperationException e) {
            IO.println("set(0, 1): UnsupportedOperationException");
        }
    }
}
Multiples.java, run with JDK 25 Python · multiples_session.py
from session import run, version

version()
run("""
java Multiples.java
""")

Output

# openjdk version "25.0.4.1" LTS
$ java Multiples.java
[7, 14, 21, 28, 35, 42, 49, 56, 63, 70]
contains 42: true, indexOf(42): 5
subList(2, 5): [21, 28, 35]
sum: 385
equals List.of(7, 14): true
set(0, 1): UnsupportedOperationException

Recorded with Python 3.14.8 on macOS 26 arm64. To run it yourself: mise exec python@3.14.8 -- python3 multiples_session.py

Multiples stores two numbers and computes each element in get(), yet it answers contains(), indexOf() and subList(), works in a for-each loop, prints itself, and compares equal to List.of(7, 14). All of those come from AbstractList and its superclass AbstractCollection, built on the two methods that Multiples implements. The last line shows a default step: AbstractList.set() throws UnsupportedOperationException, and its documentation says to override set() to make a list modifiable, and add() and remove() as well when its size may change.

java.io.InputStream works the same way. A subclass must provide read(), which returns the next byte, and the method that reads into an array of bytes calls read() repeatedly unless the subclass overrides it with something faster.

Abstract class or interface?

Both can declare methods that subclasses or implementing classes must provide. They differ in what else they can hold:

Abstract class or interface?
Criterion Abstract classInterface
Instance fields yes, with any accessnone: every field is public, static and final
Constructors yes, run through super(...)none
How many a class can have one, as its superclassany number
Methods abstract and finished ones, with any access except private abstractabstract, default, static and private ones; all but the private ones are public
When to choose Related classes that share state and code, such as a templateA capability that unrelated classes share, such as Comparable

So the deciding question is usually whether the shared part needs state. Report keeps a title and a list of sales and checks them in its constructor, which an interface cannot do. When the shared part is only a set of operations, an interface is the better choice: a class can implement several interfaces while it extends one class, so an interface never takes the single superclass a class may need for something else.

The two also combine. A common shape in the JDK is an interface for the type and an abstract class that implements part of it: List is the interface, and AbstractList is the head start for writing one. The next lesson covers interfaces, including the default methods that let them carry code.

Your turn: a template for importing data

DataImporter declares the three steps of an import, read(), problem() and save(), and the final template method importAll(), which is still empty. CsvImporter is finished, PipeImporter is a stub, and the checks use RecordingStore, a fake that writes each call into a log, so they can test the order of the steps.

The starter code and its sample checks

Java (DataImporter.java) · importer/DataImporter.java

// DataImporter.java: TODO 1.
abstract class DataImporter {
    /**
     * The template method: reads the records, checks each one, saves the good ones and rejects the others, in input
     * order. Returns how many records were saved.
     */
    final int importAll(String input, Store store) {
        // TODO 1: for each record that read(input) returns, problem(record) is null when the record is good: then
        //         call save(record, store). Otherwise call store.reject(record, problem). Count the saved records.
        return 0;
    }

    /** Step 1: splits the input into records, without headers or blank lines. */
    protected abstract String[] read(String input);

    /** Step 2: why the record cannot be saved, or null when it can. */
    protected abstract String problem(String record);

    /** Step 3: saves a good record. */
    protected abstract void save(String record, Store store);
}

Java (CsvImporter.java) · importer/CsvImporter.java

// CsvImporter.java: finished. Reads lines such as "tea,1500" (an item and its amount in paise) after a header line.
import java.util.Arrays;

class CsvImporter extends DataImporter {
    @Override
    protected String[] read(String input) {
        String[] lines = input.strip().split("\n");
        return Arrays.copyOfRange(lines, 1, lines.length);     // skips the header
    }

    @Override
    protected String problem(String record) {
        String[] fields = record.split(",");
        if (fields.length != 2) {
            return "expected 2 fields";
        }
        if (!fields[1].matches("\\d+")) {
            return "the amount is not a whole number of paise";
        }
        return null;
    }

    @Override
    protected void save(String record, Store store) {
        String[] fields = record.split(",");
        store.save(fields[0], Long.parseLong(fields[1]));
    }
}

Java (PipeImporter.java) · importer/PipeImporter.java

// PipeImporter.java: TODO 2.
class PipeImporter extends DataImporter {
    // TODO 2: lines such as "tea|1500", with no header line. Skip blank lines, and give the same reasons as
    //         CsvImporter for a record that does not have 2 fields or whose amount is not a whole number.
    @Override
    protected String[] read(String input) { return new String[0]; }

    @Override
    protected String problem(String record) { return "not written yet"; }

    @Override
    protected void save(String record, Store store) { }
}

Java (Store.java) · importer/Store.java

// Store.java: where imported records end up. Leave this file as it is.
class Store {
    void save(String item, long amountPaise) {
        IO.println("saved " + item + ": " + amountPaise + " paise");
    }

    void reject(String record, String reason) {
        IO.println("rejected \"" + record + "\": " + reason);
    }
}

Java (ImporterChecks.java) · importer/ImporterChecks.java

// ImporterChecks.java: the sample checks, with a store that records calls instead of saving.
// Run them with: java importer/ImporterChecks.java
class ImporterChecks {
    private static int passed;
    private static int total;

    static void main() {
        String csv = "item,amount\ntea,1500\ncoffee,abc\nsamosa,2000\n";
        check("the CSV importer reads 3 records after the header", new CsvImporter().read(csv).length == 3);

        RecordingStore store = new RecordingStore();
        int saved = new CsvImporter().importAll(csv, store);
        check("2 of the 3 CSV records are saved", saved == 2);
        check("the store sees the CSV records in input order", store.log().equals(
                "save tea 1500 | reject coffee,abc: the amount is not a whole number of paise | save samosa 2000"));

        store = new RecordingStore();
        saved = new PipeImporter().importAll("tea|1500\n\nvada|x\npoha|2500\n", store);
        check("2 of the 3 pipe records are saved", saved == 2);
        check("pipe records are split at the | and blank lines skipped", store.log().equals(
                "save tea 1500 | reject vada|x: the amount is not a whole number of paise | save poha 2500"));
        store = new RecordingStore();
        new PipeImporter().importAll("chai|10|20\n", store);
        check("a pipe record with 3 fields is rejected", store.log().equals("reject chai|10|20: expected 2 fields"));

        IO.println(passed + " of " + total + " checks passed");
        if (passed != total) {
            System.exit(1);
        }
    }

    static void check(String what, boolean ok) {
        total++;
        if (ok) {
            passed++;
        }
        IO.println((ok ? "PASS " : "FAIL ") + what);
    }
}

/** A fake store: it writes each call into a log instead of saving anything. */
class RecordingStore extends Store {
    private final StringBuilder log = new StringBuilder();

    @Override
    void save(String item, long amountPaise) { add("save " + item + " " + amountPaise); }

    @Override
    void reject(String record, String reason) { add("reject " + record + ": " + reason); }

    private void add(String entry) {
        log.append(log.isEmpty() ? "" : " | ").append(entry);
    }

    String log() { return log.toString(); }
}
The sample checks against the starter code Python · checks_session.py
from session import run, version

# The sample checks against the starter code in importer/.
version()
run("""
!java importer/ImporterChecks.java
""")

Output

# openjdk version "25.0.4.1" LTS
$ java importer/ImporterChecks.java
PASS the CSV importer reads 3 records after the header
FAIL 2 of the 3 CSV records are saved
FAIL the store sees the CSV records in input order
FAIL 2 of the 3 pipe records are saved
FAIL pipe records are split at the | and blank lines skipped
FAIL a pipe record with 3 fields is rejected
1 of 6 checks passed
[exit status 1]

Recorded with Python 3.14.8 on macOS 26 arm64. To run it yourself: mise exec python@3.14.8 -- python3 checks_session.py

  1. TODO 1: write importAll(). For each record that read() returns, call problem(); save the record when the answer is null, and pass it to store.reject() with the reason otherwise. Keep the input order, and return how many records were saved.
  2. TODO 2: write PipeImporter for lines such as tea|1500, with no header line. Skip blank lines, and give the same two reasons as CsvImporter.

Hint: String.split takes a regular expression, and in a regular expression | means “or”, so record.split("|") splits between every pair of characters. Write record.split("\\|").

When the program prints 6 of 6 checks passed, both importers run through the one template you wrote, and a third format would need only its three steps.

Check yourself

6 questions about this lesson. Every answer and why it is right is on the page, behind “Show the answer”. Your score stays in this browser.

  1. Question 1 of 6 What happens with this code?

    Read the code, then choose one answer.

    abstract class Shape {
        abstract double area();
    }
    class Square extends Shape {
        double side = 2;
    }
    // in main:
    IO.println(new Square().side);
    Show the answer to question 1

    Answer: It does not compile, because Square neither implements area() nor is declared abstract

    A class that is not abstract must implement every abstract method it inherits. The compiler checks this when it compiles Square, whatever main does: "Square is not abstract and does not override abstract method area() in Shape".

  2. Question 2 of 6 Which of these declarations compile?

    Choose every answer that is right.

    Show the answer to question 2

    Answer:

    • abstract class Fee { Fee() { } }
    • abstract class Fee { void apply() { } }

    An abstract class may have constructors, which its subclasses call through super(...), and it does not need any abstract method at all. abstract and final contradict each other: one needs subclasses, the other forbids them. An abstract method may only sit in an abstract class, and it may not be private, because a private method can never be overridden.

  3. Question 3 of 6 What does this print?

    Read the code, then choose one answer.

    abstract class Greeting {
        final String text(String name) {
            return opening() + ", " + name + (polite() ? ". Thank you." : ".");
        }
        protected abstract String opening();
        protected boolean polite() { return true; }
    }
    class Short extends Greeting {
        @Override protected String opening() { return "Hi"; }
        @Override protected boolean polite() { return false; }
    }
    // in main:
    IO.println(new Short().text("Asha"));
    Show the answer to question 3

    Answer: Hi, Asha.

    text() is the template method, inherited by Short. It calls opening(), which Short implements, and the hook polite(), which Short overrides to return false, so the thank-you is left out. final only stops subclasses from replacing text(); they still inherit and use it.

  4. Question 4 of 6 Why does Report declare its template method render() as final?

    Choose one answer.

    Show the answer to question 4

    Answer: So that subclasses supply the steps but cannot reorder or skip them by replacing render()

    A template method's job is to fix the steps and their order in one place. If a subclass could override it, it could skip the header or forget the total, and every caller of render() would have to trust each subclass. Calling abstract methods needs no final, and final forbids overriding instead of forcing it.

  5. Question 5 of 6 Your payment classes each already extend a different vendor's base class, and you want all of them to offer refund(long paise). What do you declare?

    Choose one answer.

    Show the answer to question 5

    Answer: An interface Refundable, which each class implements next to its superclass

    A class has exactly one superclass, and these classes already use theirs, so a second abstract class cannot be added to them. A class may implement any number of interfaces, which is why capabilities such as "can refund" are usually interfaces.

  6. Question 6 of 6 What does this print?

    Read the code, then choose one answer.

    import java.util.AbstractList;
    
    class Squares extends AbstractList<Integer> {
        public Integer get(int i) { return i * i; }
        public int size() { return 4; }
    }
    // in main:
    IO.println(new Squares().indexOf(9) + " " + new Squares());
    Show the answer to question 6

    Answer: 3 [0, 1, 4, 9]

    AbstractList implements indexOf() and the other List methods on top of get() and size(), so indexOf(9) walks the values 0, 1, 4 and 9 and finds 9 at index 3. toString() is inherited from AbstractCollection, which lists the elements, so the list prints as [0, 1, 4, 9].

Interview questions

Fresher · Can an abstract class have a constructor, and why would it have one?

Yes. An abstract class cannot be instantiated with new, but every object of one of its concrete subclasses contains the abstract class’s part, and that part is initialised by the abstract class’s constructor, which each subclass constructor runs through super(...). A constructor there lets the abstract class set up and check the state it owns, so the rule is written once: in this lesson, Report refuses a blank title or an empty list of sales for every kind of report. Such constructors are often protected, since only subclasses call them. A good answer adds that interfaces have no constructors and no instance fields, which is one of the reasons to choose an abstract class.

Model answers open with any pass: ₹79 for 1 week ($2.99), one payment that never renews.

  1. Fresher to mid

    You are writing payment providers that share retry logic and a count of failed attempts, and one provider must also extend a vendor's client class. Would you use an abstract class, an interface or both? Defend the choice.

    Model answer, Pro. Model answers open with any pass; prices are in the notice on this page. First step and follow-ups

  2. Mid to senior

    Your abstract DataImporter has a final importAll() template and 12 subclasses in three teams' code. You must add an audit step after every saved record, and one subclass needs to skip some records after reading them. How do you change the template without breaking the 12 subclasses?

    Model answer, Pro. First step and follow-ups

Key takeaways

  • abstract class makes a class that cannot be instantiated; its abstract methods have no body, and every concrete subclass must implement them.
  • An abstract class can have fields, constructors and finished methods, and its constructor runs, through super(...), for every subclass object.
  • abstract cannot be combined with final, private or static.
  • A template method is a final method that fixes the steps of a job and calls abstract methods and hooks for the steps that differ; add new steps as hooks with defaults.
  • AbstractList turns get() and size() into a complete read-only List; InputStream builds its other read methods on read().
  • Choose an abstract class for shared state and code, an interface for a capability that unrelated classes share, and often both: an interface for the type and an abstract class as a starting point.

References

Report a problem with this lesson

Quick answers and tool search

Type to search tools or to get a quick answer, for example 18% of 2500. Use the up and down arrow keys to move through the results, Enter to choose, and Escape to close.