An abstract class is intentionally incomplete: it states what concrete subtypes must provide while keeping shared state and behaviour in one place. In the Shape hierarchy below, subclass obligations, dispatch through a base reference and the boundary between an abstract class and an interface become concrete. These ideas connect inheritance, polymorphism and interface design across KnowledgeGate’s Coding & Skill Development Courses.
Abstract class in OOP: incomplete by design
An abstract class cannot be instantiated directly, but may declare bodyless operations alongside fields, constructors and implemented methods. Its incompleteness is deliberate.
A concrete subclass must implement every accessible inherited abstract operation; otherwise, it must also be abstract. A base reference such as Shape s is legal, but must point to a concrete subtype object.
Keep five terms clear:
Abstract class: an incomplete base type.
Abstract method: an operation a subclass must implement.
Concrete method: a method with an implementation.
Concrete subclass: a directly instantiable completed class.
Abstract subclass: a subclass that still leaves an obligation unresolved.
Syntax varies by language, but the contract-and-completion idea stays the same.
Abstract classes define an interface and reuse implementation
Here, saying an abstract class defines an interface means the public contract visible to clients, not necessarily Java's separate interface keyword. Shape clients can rely on area(), perimeter() and report() without knowing each subtype's formula.
The design performs two jobs. Abstract operations mark variation points each subtype must fill; the id field, constructor and concrete report() method provide shared state and reusable behaviour. Thus, an abstract class offers more than method signatures.
This uses abstraction, inheritance and runtime polymorphism, while the private field protects state. OOP for Teaching CS Exams: Classes and Inheritance recaps those relationships. One base type can enforce a contract and carry reusable implementation.
Abstract class worked example: complete the Shape contract
Consider this Java-like hierarchy. Integer dimensions keep every result exact, and the concrete report() method deliberately calls the abstract operations.
abstract class Shape {
private final int id;
Shape(int id) { this.id = id; }
abstract int area();
abstract int perimeter();
String report() {
return "S" + id + ": A=" + area() + ", P=" + perimeter();
}
}
class Rectangle extends Shape {
private final int width, height;
Rectangle(int id, int width, int height) {
super(id); this.width = width; this.height = height;
}
int area() { return width * height; }
int perimeter() { return 2 * (width + height); }
}
class RightTriangle extends Shape {
private final int a, b, c;
RightTriangle(int id, int a, int b, int c) {
super(id); this.a = a; this.b = b; this.c = c;
}
int area() { return a * b / 2; }
int perimeter() { return a + b + c; }
}Instantiate both objects through the abstract type:
Shape r = new Rectangle(7, 6, 4);
Shape t = new RightTriangle(8, 3, 4, 5);Shape owns the shared id and report() implementation; each subtype owns its dimensions and formulas. Both subclass constructors call super(id), so the abstract base constructor initialises common state. Because r and t share the reference type Shape, clients use one stable set of operations for both.
For the rectangle, area is 6 x 4 = 24, and perimeter is 2 x (6 + 4) = 2 x 10 = 20. Therefore, r.report() returns S7: A=24, P=20.
For the right triangle, area is 3 x 4 / 2 = 12 / 2 = 6, and perimeter is 3 + 4 + 5 = 12. Therefore, t.report() returns S8: A=6, P=12.
The 3, 4, 5 sides form a right triangle, so using the perpendicular sides in a * b / 2 is valid. The base method builds each string after runtime dispatch obtains the subtype results. new Shape(1) is illegal because Shape is abstract, even though its constructor exists.

Abstract references and dynamic dispatch: trace the exact calls
Trace r.report() in three steps. First, Shape supplies the concrete body of report(). Second, its call to area() dispatches to Rectangle.area() and returns 24. Third, its call to perimeter() dispatches to Rectangle.perimeter() and returns 20. For t.report(), the same inherited body dispatches to the triangle methods, which return 6 and 12.
Now process both objects uniformly:
Shape[] shapes = {r, t};Starting from zero, total area is 0 + 24 + 6 = 30. Total perimeter is 0 + 20 + 12 = 32. The reference type controls which operations are available to the caller, while the runtime object selects the overriding implementation.
Declaring Shape r creates a reference to a Rectangle, not an abstract object. Calls made inside the inherited report() still dispatch dynamically to the concrete subtype's area() and perimeter() methods.

Incomplete subclasses: one missing method keeps the class abstract
An intermediate subclass may complete only part of the contract:
abstract class AreaOnlyShape extends Shape {
AreaOnlyShape(int id) { super(id); }
int area() { return 10; }
}AreaOnlyShape implements area() but inherits the unresolved perimeter(), so it must remain abstract. new AreaOnlyShape(9) is still illegal. A further subclass can finish the work:
class FixedShape extends AreaOnlyShape {
FixedShape(int id) { super(id); }
int perimeter() { return 14; }
}Now Shape f = new FixedShape(9); is legal, and f.report() returns S9: A=10, P=14.
Declaration | Legal? | Reason |
|---|---|---|
| Yes | It declares only a reference. |
| No | The target class is abstract. |
| Yes | Both inherited abstract operations are implemented. |
Constructor chaining is separate from direct instantiation. Constructing a FixedShape runs FixedShape(9), then AreaOnlyShape(9), then the Shape constructor, which stores id = 9. The resulting object is concrete.
Abstract class versus interface: choose shared identity or a capability
Question | Abstract class | Interface type |
|---|---|---|
Purpose | Shared base and partial implementation | Capability contract |
Per-object state | Can hold ordinary instance fields | Has no ordinary per-object instance state |
Construction | May define a constructor for subclass initialisation | Has no constructor |
Implementation reuse | Concrete methods | Language-supported defaults, where available |
Inheritance choice | Uses the class inheritance relationship | Combines with other capabilities |
In Java specifically, a class extends one class but may implement multiple interfaces. Modern Java interfaces may also contain default, static and private methods under the language's rules.
Choose abstract Shape when every shape shares id and report(). Choose interface Printable { String print(); } when unrelated classes should promise the same capability. In C++, a pure virtual function such as virtual int area() const = 0; expresses the analogous incomplete-operation idea. The C++ Tutorial places that syntax in its wider context.
Abstract class exam patterns and traps
Typical questions ask which declarations and instantiations are legal, how many inherited abstract methods remain, which method dynamic binding selects, whether a base constructor runs, or whether an abstract base or interface fits.
Rapid check:
new Shape(1)is illegal becauseShapeis abstract.Shape r = new Rectangle(7, 6, 4)is legal because the object is concrete.r.report()returnsS7: A=24, P=20.AreaOnlyShapemust stay abstract becauseperimeter()is missing.Across
randt, total area is30and total perimeter is32.
Remember the traps. An abstract class may contain concrete methods and constructors. A concrete subclass must complete every inherited abstract operation. An abstract reference is not an abstract object. In design prose, interface does not always mean Java's interface keyword.
Abstract class in OOP: the short version and next step
Recall four lines: an abstract class means no direct object; an abstract method means a subclass obligation; a concrete base method means reusable behaviour; a base reference plus a concrete object enables dynamic dispatch. Rectangle(6,4) gives area/perimeter 24/20, RightTriangle(3,4,5) gives 6/12, and the totals are 30/32.
Rewrite the hierarchy by hand, remove perimeter(), predict the compiler response, then restore it and trace report(). If you want a wider foundation across C, C++, Java, Python and OOP, continue with Coding For Placements.




