Concepts / Abstract Base Classes and Interfaces

Abstract Base Classes and Interfaces

Inheritance does not make every class change flow in every direction.

  • Programming

One Collection, Many Roles

Imagine counting everyone in a school building. One approach is to count teachers, students, and administrators separately. A polymorphic approach treats each person as a SchoolMember, so one piece of code can process them together without knowing each person's specific role. The objects still retain their specialized behavior, but the surrounding code can work with the shared parent type.

Definition

Polymorphism allows a subtype to be treated as an instance of its parent type. This enables flexible code that works with multiple subtypes without checking which specific subtype each object belongs to.

Teacherschool memberSchoolMemberparent typeStudentschool membercount membersone shared processis ais aaccepted as
How can objects from different subtypes be passed to the same piece of code and treated uniformly as instances of their parent type?

The Runtime Method Choice

A parent-type reference can point to an object created from a subtype. When a method is called through that reference, dynamic dispatch chooses the method according to the object's actual runtime type, not according to the parent type used by the reference. If a SchoolMember reference holds a Teacher object, calling tell() runs the Teacher version of tell(). If the reference holds a Student object, the Student version runs.

Worked example

Calling tell() Through a Parent Type

A collection is treated as a collection of SchoolMember objects, but it contains one Teacher and one Student. What happens when the same tell() operation is applied to each object?

  1. 1
    Shared treatment

    The surrounding operation handles both objects as SchoolMember instances. It does not need separate logic for Teacher and Student.

  2. 2
    First runtime type

    For the Teacher object, dynamic dispatch selects Teacher's version of tell().

  3. 3
    Second runtime type

    For the Student object, dynamic dispatch selects Student's version of tell().

  4. 4
    Control flow

    The operation remains the same even though the behavior supplied by each object is different.

Result

One parent-oriented operation can invoke the correct subtype behavior for each object at runtime.

SchoolMemberobjectsTeacher and Studentcall tell()same operationTeacher.tell()Teacher behaviorStudent.tell()Student behavioreach objectruntime type Teacherruntime type Student
How does control flow remain the same when a collection contains different subtypes, while each object still provides its own behavior?

Inheritance as Shared Functionality

Inheritance creates a second important effect: functionality added to a parent class automatically becomes available to all of its subtypes. For example, if an ID card field is added to SchoolMember, both Teacher and Student objects gain access to that field without either subtype needing to be changed.

SchoolMemberID card fieldTeacherinherits ID cardStudentinherits ID cardparent ofparent of
When functionality is added to a parent class, how does that functionality become available to all of its subtypes?
Key point

A subtype object contains the parent's data along with any additional data defined by the subtype. This layered structure explains why parent functionality can operate on a subtype object: the parent portion of that object is present as part of the subtype object.

Changes That Stay Local

Inheritance does not make every class change flow in every direction. A change made to the parent is available to its subtypes, but a change made only to one subtype remains isolated. If a new method is added to Teacher, that method does not automatically appear in Student or in SchoolMember. This isolation lets you specialize one subtype without accidentally changing sibling subtypes or the parent.

SchoolMembershared behaviorTeacherexisting behaviorSchoolMembershared behaviorTeachernew methodStudentexisting behaviorStudentexisting behaviorparent ofparent ofparent ofparent of
What changes when behavior is added or modified in one subtype, and what remains unchanged in the parent class and sibling subtypes?

Designing for Extension

Polymorphism allows one function or operation to work with the parent type rather than requiring a separate version for every subtype. When a new subtype is added, existing code can continue to work without modification, provided the new subtype can be treated as the parent type. This supports the Open-Closed Principle: code is open for extension because new subtypes can be added, and closed for modification because existing processing code does not need to change.

In the real world

Suppose a school-reporting operation processes SchoolMember objects. It can accept teachers and students through the same parent-oriented operation. Adding another SchoolMember subtype later does not require the reporting operation to add a new type-specific branch. The new subtype supplies its own behavior through dynamic dispatch while the surrounding operation remains unchanged.

  • Common mistake

    Assuming the parent type determines which overridden method runs.

    What it looks like

    A SchoolMember reference holds a Teacher object, but the learner expects the generic SchoolMember version of tell() to run.

    Why it fails

    Dynamic dispatch selects the method using the actual runtime type of the object.

    Do this instead

    Track the object itself. A Teacher object causes Teacher's version of tell() to run, even when accessed through a SchoolMember reference.

  • Common mistake

    Writing separate processing logic for every subtype when the operation only needs the parent type.

    What it looks like

    Counting teachers and students in separate paths even though both can be treated as SchoolMember objects.

    Why it fails

    This gives up the uniform processing that polymorphism provides.

    Do this instead

    Write one operation around the parent type and let each subtype provide its own runtime behavior.

  • Common mistake

    Expecting a subtype-only method to appear in sibling subtypes.

    What it looks like

    Adding a method to Teacher and then expecting Student to have that method.

    Why it fails

    Subtype changes remain isolated.

    Do this instead

    Add shared functionality to the parent when every subtype should receive it; keep specialized functionality in the subtype that owns it.

  • Common mistake

    Expecting a parent-class change to affect only one subtype.

    What it looks like

    Adding an ID card field to SchoolMember while expecting it to be available only on Teacher.

    Why it fails

    Functionality added to the parent automatically propagates to all subtypes.

    Do this instead

    Place functionality in the parent only when it should be shared by its subtypes.

Practice the Prediction

MEDIUM

A collection is processed as SchoolMember objects. It contains a Teacher and a Student. The Teacher subtype receives a new method that is not added to SchoolMember. Predict which classes can use that new method and what happens when the shared tell() operation is called for each object.

Hints
  • Separate the effect of a subtype-only change from the effect of dynamic dispatch.
  • Ask which class owns the new method.
  • For tell(), identify the actual runtime type of each object.
Worked example

Checking the Prediction

Determine the effect of adding a Teacher-only method while processing a Teacher and a Student through the SchoolMember type.

  1. 1
    Teacher-only method

    The new method belongs to Teacher, so it is available to Teacher and is not automatically added to Student or SchoolMember.

  2. 2
    Shared operation

    The collection can still be processed through the SchoolMember type because both objects are SchoolMember subtypes.

  3. 3
    Teacher behavior

    Calling tell() for the Teacher object selects Teacher's version through dynamic dispatch.

  4. 4
    Student behavior

    Calling tell() for the Student object selects Student's version through dynamic dispatch.

Result

The Teacher-only method remains isolated, while the shared tell() operation continues to use each object's correct subtype behavior.

Polymorphism in Practice

  1. 1
    A subtype can be treated as an instance of its parent type, allowing one operation to work with multiple subtypes.
  2. 2
    Dynamic dispatch chooses the correct subtype method at runtime based on the actual object type, not the parent type of the reference.
  3. 3
    Functionality added to a parent class becomes available to all of its subtypes.
  4. 4
    A change made only in one subtype remains isolated from the parent class and sibling subtypes.
  5. 5
    Polymorphism supports code that is open for extension and closed for modification.

Key Takeaways

  • Polymorphism lets code treat Teacher, Student, and other subtypes uniformly as SchoolMember objects.
  • Dynamic dispatch ensures that each object supplies the correct subtype implementation at runtime.
  • Parent functionality propagates to subtypes, while subtype-specific changes remain local.
  • Uniform parent-oriented processing reduces type-specific logic and supports the Open-Closed Principle.

Related concepts

More concepts starting with A