Abstract Classes
An abstract class is declared with the abstract modifier and is meant to be subclassed rather than instantiated on its own.
A Drawing Program Full of Shapes
Imagine a drawing program that holds several kinds of shapes on screen: rectangles, ovals, and rounded rectangles. Each of these knows how to paint itself, and each does so through its own redraw() method. All of these shapes share a common superclass, which we can call Shape. The design question is what redraw() should do inside Shape itself.
This article follows that question to its answer. The answer is a useful idea in object-oriented design: some classes exist only to describe what their subclasses have in common, and they are never meant to be turned into objects. Working through why that is true explains the purpose of abstract classes and abstract methods. As you read, keep one question in mind throughout: which parts of Shape are complete, and which parts are left for someone else to finish?
Tracing the Shape Class
The source builds its discussion around a short Java program based on a Shape class. Trace the class below before reading the explanation. The setColor method is fully written: it stores a new color in the color instance variable and then calls redraw. The redraw method is different. It has no body at all, because its declaration ends with a semicolon.
Read the class with the trace in mind. The part that is complete is setColor: every subclass of Shape inherits working color-changing behavior without writing it again. The part that is unfinished is redraw: the class declares that the operation exists, but it does not say how a shape is drawn. That unfinished part is what the rest of this article is about.
Why Shape Cannot Draw Itself
Whenever a particular shape must draw itself, the redraw() method belonging to that shape's own class is the one that runs. The redraw() inside Shape is never the one doing the drawing for a rectangle or an oval. That raises a puzzle: if Shape's version will not really be used, what should it contain?
The honest answer is to leave the body empty. Shape stands for the general notion of a shape, and there is no real way to draw a general notion. Only specific shapes, such as a rectangle or an oval, can actually be drawn. Leaving the body blank is therefore not laziness. It reflects the fact that no sensible drawing instructions exist at this level of the hierarchy.
But the method cannot simply vanish. Other code in Shape, such as setColor(), calls redraw(). A program may also hold a variable declared as Shape and call redraw() on it. The compiler checks these calls against the declared type, not against the object that happens to be stored there. If Shape had no redraw() at all, those calls would be rejected. So the method must be declared in Shape even though its body has nothing to contribute.
A superclass may need a method that has no meaningful body because the method is part of the shared vocabulary of every subclass, even though only the subclasses can say how the operation actually works.
Declaring the Abstract Class and Method
The two declarations in this section use the same keyword, abstract, in different places. The keyword changes what the compiler will accept. Each part is covered below.
Calling redraw Through a Shape Variable
Programs need variables whose type is Shape. Such a variable can refer to a rectangle, an oval, or any other specific shape, and a call made through it always reaches an object from one of Shape's subclasses. The sequence below shows the flow for one call. The caller asks for the operation through the Shape variable, and the work is done by the subclass object.
Drawing a rectangle in redTrace the run step by step. The variable s is declared as Shape, but the object it refers to is a Rectangle. The call s.setColor("red") runs the complete method inherited from Shape. That method stores red in color and then calls redraw(). Because the object is a Rectangle, the redraw() belonging to Rectangle is the one that runs, and it prints the message shown in the output. Shape never supplies a drawing body of its own.
Where a Hierarchy Breaks the Rules
The hierarchy has a clear shape. Shape is the general parent, and Rectangle, Oval, and RoundRect are specific children. The top node can never be built directly, while each lower node can. An abstract method declared in the top node must therefore be given a body in each lower node before an object of that lower type can be created and used. The diagram below marks the place where that chain of obligations breaks.
Suppose the programmer adds a new shape, a triangle, by writing a Triangle class that extends Shape. Because Shape declares redraw() as abstract, Triangle must supply a redraw() implementation before it can be a concrete class. The class below omits that method.
Triangle is not concrete in its current form, so the program will not accept an attempt to create a Triangle object. The fix is to add a redraw() method to Triangle. The expectation that Triangle would be finished was not met at the point where the implementation is missing, and that is the point to inspect.
Abstract Versus Concrete Classes
The two kinds of class sit at different levels of a hierarchy. An abstract class sits above and describes common properties. Concrete classes sit below it and supply everything needed to make a working object. The table summarizes the contrast.
| Feature | Abstract class (Shape) | Concrete class (Rectangle) |
|---|---|---|
| Declaration | Uses the abstract modifier | No abstract modifier |
| Can create objects with new | No | Yes |
| Can be the parent of other classes | Yes, that is its purpose | Yes, but it is also complete |
| Abstract methods | May declare them with a semicolon instead of a body | Must provide a body for every inherited abstract method |
| Role | Shares common properties and partial implementation | Supplies everything needed for a working object |
Abstract and concrete classes compared by what they declare and what can be constructed from them
Mistakes Beginners Make With Abstract Classes
Common mistake Trying to create an object of an abstract class
What it looks likeShape s = new Shape();Why it failsAn abstract class is not used to construct objects. It exists as a basis for making subclasses and to express the properties they share.
Do this insteadCreate an object of a concrete subclass such as new Rectangle(), and store it in a Shape variable if needed.
Common mistake Deleting the abstract method declaration because its body is empty
What it looks likepublic class Shape { public void setColor(String c) { color = c; redraw(); } }Why it failsThe call to
redraw()still needs a declaration in Shape, and the compiler checks calls against the declared type.Do this insteadKeep the declaration as public abstract void redraw(); so the call is legal and the subclasses are obliged to implement it.
Common mistake Assuming the superclass version of an abstract method will run
Why it failsThe abstract method has no body, and the
redraw()belonging to the object's own class is the one that runs.Do this insteadLook at the concrete subclass of the object being used and check its implementation.
Common mistake Forgetting that a concrete subclass must implement every inherited abstract method, not just the ones it wants
Finding the Missing Implementation
What do you think happens?
Given that Shape is declared abstract, what happens when a program runs Shape s = new Shape();?
Reveal answer
The compiler rejects it because Shape is abstract.
An abstract class is not used to construct objects. Only concrete classes can be used to create objects, so new Shape() is not allowed. Objects must be created from a concrete subclass such as Rectangle.
A class Circle extends Shape and declares no redraw() method. Is Circle concrete? Can you write new Circle()? What single change would make Circle usable?
Hints
- Ask whether Circle is declared abstract. If it is not, it is concrete.
- Check whether Circle supplies an implementation for every abstract method it inherits from Shape.
- Adding a
redraw()method with a body to Circle is the change that satisfies the obligation.
Recapping the Three Ideas
- 1The abstract modifier marks a class as meant to be subclassed rather than instantiated.
- 2An abstract method has no body and ends with a semicolon, and it is declared so that calls through the superclass type are legal.
- 3Every concrete subclass must supply an implementation for each abstract method it inherits, and a missing implementation is the most likely place to look when a hierarchy behaves unexpectedly.
Key Takeaways
- The abstract modifier on a class marks it as incomplete on purpose, so it is subclassed rather than used to construct objects.
- An abstract method is declared with the abstract keyword and ends with a semicolon instead of a body, and it stays declared so calls through the superclass type are legal.
- A superclass may hold an abstract method with no meaningful body because only the concrete subclasses can say how the operation works.
- Every concrete subclass must implement each inherited abstract method, so a missing implementation is the first place to look when a hierarchy misbehaves.