AP Computer Science A

Lesson 13: Abstract Classes and InterfacesAP

🎧 Listen to this lesson

Abstract classes and interfaces are Unit 9's design instruments: both let you define behaviour that other classes must supply, and both are used polymorphically — code written against the abstract type drives concrete subclasses it has never seen. The exam tests them as design judgment ("which mechanism fits this situation, and what code compiles?") more than as syntax, so this lesson concentrates on the exact rules and the decision between them.

Abstract classes: a class that is deliberately unfinished

An abstract class is declared with the abstract keyword. It may contain everything a normal class has — fields, constructors, concrete methods — plus abstract methods: declarations with a signature and no body, ending in a semicolon.

public abstract class Shape
{
    private String color;

    public Shape(String c)
    {
        color = c;
    }

    public String getColor()
    {
        return color;
    }

    public abstract double area();     // no body — subclasses MUST provide one
}

public class Circle extends Shape
{
    private double radius;

    public Circle(String c, double r)
    {
        super(c);
        radius = r;
    }

    public double area()
    {
        return Math.PI * radius * radius;
    }
}

The rules that matter:

  • You cannot instantiate an abstract class. new Shape("red") does not compile — the class is admittedly incomplete. (A reference is fine: Shape s = new Circle(...) is the standard polymorphic move.)
  • A subclass must implement every inherited abstract method, or itself be declared abstract (deferring the duty to *its* subclasses).
  • Abstract classes have constructors, called through super(...) by subclasses — the Circle example uses one to initialise the shared color field.
  • Shared state and shared concrete code live in the abstract class; that is its whole advantage over an interface.

Interfaces: pure contracts

An interface declares what a class can *do*, without saying how. In the AP subset, an interface contains method signatures (implicitly abstract) and sometimes constants; a class implements it and must supply every method.

public interface Comparable
{
    int compareTo(Object other);
}

compareTo is the one interface the exam truly cares about. Its contract: return a negative value if this object comes before the other, zero if they are equal in ordering, positive if after. Strings implement it lexicographically; your own classes implement it to define a natural order (by price, by name, by score). Sorting code, and questions about ordering, then work for any Comparable — the same "code against the general type" idea as Unit 8, one level up.

Multiple interfaces are allowed — class X implements A, B — while inheritance remains single. And interfaces are types: List is an interface that ArrayList implements, which is why List<String> list = new ArrayList<String>(); is legal and is considered the professional declaration style.

Choosing: abstract class or interface?

QuestionAbstract classInterface
Relationshipis-a, within one family treecan-do, across unrelated families
Shared fields/codeyesno (in the AP subset)
How many can a class take?one superclassmany interfaces
Typical examplesShape → Circle, RectangleComparable, List

Rule of thumb: if the new type is a *kind of* the general thing and relatives share real implementation, extend an abstract class. If the capability cuts across families (a Book, a Song and a Product can all be *priced/sorted*), define an interface.

Trace focus: what actually runs

Abstract types change nothing about dispatch: Shape s = new Circle("red", 2.0); s.area(); runs Circle's area() — the actual type decides, exactly as in Lesson 8. s.getColor() runs the concrete method inherited from Shape. The compile-time check still uses the declared type: Shape declares both methods, so both calls are legal.

Real-world design: contracts let strangers cooperate

Java's ecosystem runs on interfaces: database drivers, payment gateways and sorting utilities are specified as interfaces, and companies ship implementations the original designers never saw. When Collections-style library code sorts your objects, it asks only that they be Comparable — your class promises a sensible compareTo, the library promises a correct sort, and neither side knows anything else about the other. That is a contract in the legal sense, enforced by the compiler.

Key idea. Key idea: abstract class = partially built class in a family (shared code, one per class, cannot be instantiated); interface = pure capability contract (no shared code, many per class, the basis of Comparable and List).

Common mistakes (from examiner reports)

  • Trying to instantiate an abstract class or an interface. References yes, new no.
  • Leaving an abstract method unimplemented in a concrete subclass — a compile error, and a common one when the subclass implements *most* methods.
  • Giving an abstract method a body (even an empty { } makes it a concrete method with very different meaning) or forgetting abstract on the class itself.
  • Wrong compareTo sign convention. Negative means "I come first". Reversed signs invert every sort built on the method.
  • Making interface implementations non-public. Interface methods are public; an implementing method with weaker access does not compile.
  • Reaching for an abstract class when unrelated classes need the capability — the design-judgment questions are scored on exactly this distinction.
Try it. Try it: Shape s = new Shape("blue"); — what goes wrong, and what is the smallest legal change? (It does not compile: Shape is abstract. Change the right-hand side to a concrete subclass, e.g. new Circle("blue", 1.0).)

AP Exam Experience

Question 1. Consider the following declarations.

public abstract class Pet
{
    public abstract String speak();
}

public class Cat extends Pet
{
    public String speak()
    {
        return "Meow";
    }
}

Which of the following statements will not compile?

  • (A) Pet p = new Cat();
  • (B) Pet p = new Pet();
  • (C) Cat c = new Cat();
  • (D) System.out.println(new Cat().speak());
  • (E) Pet[] pets = new Pet[3];

Answer: (B). Abstract classes cannot be instantiated — that is the definition. (A) is the intended polymorphic usage; (E) creates an array of *references* (all initially null), which is perfectly legal.

Question 2. A class Song is declared as public class Song implements Comparable. Inside Song, the method is written as:

int compareTo(Object other)
{
    Song o = (Song) other;
    return title.compareTo(o.title);
}

Which of the following best describes this code?

  • (A) It works correctly: Song objects can now be compared by title.
  • (B) It does not compile because the compareTo method must be declared public.
  • (C) It does not compile because compareTo must return a boolean.
  • (D) It compiles, but the cast to Song is unnecessary.
  • (E) It does not compile because Song must extend Object before implementing an interface.

Answer: (B). Methods declared in an interface are implicitly public, and an implementing method cannot reduce visibility. compareTo returns an int by contract, so (C) is wrong; the cast in (D) is in fact required, because the parameter arrives as Object.

Question 3. Consider the following code segment, where a, b and c are String values "apple", "banana" and "avocado" respectively.

int x = a.compareTo(b);
int y = c.compareTo(a);
int z = b.compareTo(b);

Which of the following correctly describes the signs of x, y and z?

  • (A) x is negative, y is negative, z is zero
  • (B) x is positive, y is positive, z is zero
  • (C) x is negative, y is positive, z is zero
  • (D) x is positive, y is negative, z is zero
  • (E) All three values are zero

Answer: (C). "apple" precedes "banana" alphabetically, so x < 0. "avocado" follows "apple" ('v' > 'p' at the second character), so y > 0. Any value compared with itself gives 0. The exam tests the sign, never the exact magnitude — a value like −1 or −8 are both correct signs.

Free-Response Question. This question involves a pricing system for a store. (a) Write the complete class Product that implements Comparable. A product has a private String name and a private double price, set by a constructor taking the name and price in that order. It has a method getPrice() returning the price. Its compareTo method must order products by price from lowest to highest (a product with a lower price "comes first"). If two products have the same price, they are considered equal for ordering. (b) Write the static method cheapest, which takes an array of Product objects (assume at least one element) and returns the Product with the lowest price, using compareTo to make the comparisons.

Scoring guideline (9 points). Part (a) — 6 points:

  • +1 Class header declares Product implements Comparable
  • +1 Private fields String name and double price declared
  • +1 Constructor takes (String, double) in order and initialises both fields
  • +1 getPrice() returns the price as a double
  • +1 compareTo(Object) correctly casts the parameter and accesses the other product's price
  • +1 compareTo returns a negative value when this price is lower, zero when equal, positive when higher (any implementation honouring the sign contract, e.g. comparing with < / > or Double.compare-free arithmetic on the prices)

Part (b) — 3 points:

  • +1 Correct method header: static, returns Product, one Product[] parameter
  • +1 Traverses all elements, initialising the best candidate to the first element
  • +1 Uses compareTo to replace the best candidate whenever a strictly cheaper product is found, and returns it

Model solution.

public class Product implements Comparable
{
    private String name;
    private double price;

    public Product(String n, double p)
    {
        name = n;
        price = p;
    }

    public double getPrice()
    {
        return price;
    }

    public int compareTo(Object other)
    {
        Product o = (Product) other;
        if (price < o.price)
        {
            return -1;
        }
        else if (price > o.price)
        {
            return 1;
        }
        return 0;
    }
}

public static Product cheapest(Product[] arr)
{
    Product best = arr[0];
    for (int i = 1; i < arr.length; i++)
    {
        if (arr[i].compareTo(best) < 0)
        {
            best = arr[i];
        }
    }
    return best;
}


## Going deeper: the decision, worked on a real specification
A school wants software for its sports day. Requirements: (1) every participant — runners, jumpers, swimmers — has a name and an age, stored and initialised the same way; (2) every participant must be able to produce an entry code, but the code format differs completely per sport and shares no code; (3) the drama club (unrelated to sports) also needs entry codes for its performers.
Apply the table from this lesson. Requirement (1) is shared state and shared initialisation inside one family → an **abstract class** `Participant` holding `name`/`age`, a constructor, and accessors. Requirement (2) is a capability with no shared implementation → declare `entryCode()` as an **abstract method** in `Participant` (family-internal) AND, because of requirement (3), also define an **interface** `Codable` with `entryCode()` that both `Participant` and the drama club's `Performer` class can implement. The exam's design questions are graded on matching mechanism to requirement exactly this way: shared code pulls toward abstract classes; cross-family capability pulls toward interfaces; a scenario with both forces rewards using both.

📊 Interactive

Interactive — hover or explore the chart above.

Keep reading to complete this lesson