Overview

Object-oriented programming (OOP) organizes software around objects — entities that combine data (state) and functions (behavior).
Languages like Java, C++, and Python implement OOP through classes, inheritance, and method dispatch systems.

While OOP is sometimes treated as a paradigm shift, it’s fundamentally a set of language-level abstractions for modularity, polymorphism, and code reuse.

Note

Objects ≈ records with attached procedures.
Classes define blueprints; objects are instances with runtime identity.


Classes and Objects

A class defines:

  • Fields (state)
  • Methods (behavior)
  • Visibility and access rules
  • Optionally, inheritance relationships

An object is an instance of a class:

  • Has its own field values.
  • Shares method definitions with others of the same class.

Example

class Shape {
  double area() { return 0.0; }
}
 
class Circle extends Shape {
  double r;
  Circle(double r) { this.r = r; }
  double area() { return Math.PI * r * r; }
}

Circle inherits area() from Shape but overrides it with a specialized implementation.

Tip

In OOP terms: Shape defines an interface; Circle provides a concrete realization.


Inheritance

Inheritance allows a new class to reuse and extend the definition of an existing class.

Single Inheritance

Most languages (Java, Python) allow only one direct superclass:

Shape
  └── Circle

Multiple Inheritance

Some languages (C++, Eiffel) allow multiple:

class FlyingFish extends Animal, Swimmer, Flyer

Warning

Multiple inheritance introduces the diamond problem — ambiguity when two superclasses define the same method or field.

Composition vs Inheritance

  • Inheritance models an “is-a” relationship (Circle is a Shape).

  • Composition models a “has-a” relationship (Car has an Engine).

Example

Diagram idea (composition_vs_inheritance.svg)
Two panels showing:

  • Inheritance arrows (is-a)

  • Composition arrows (has-a), using field inclusion rather than subclassing.


Subtyping and Polymorphism

Nominal vs Structural Subtyping

  • Nominal: based on declared inheritance (class Circle extends Shape).

  • Structural: based on member compatibility (as in Go or TypeScript).

Subtype Polymorphism

A subclass instance can be used where a superclass is expected:

Shape s = new Circle(5);
System.out.println(s.area());

The method invoked depends on the runtime type, not the static type — that’s dynamic dispatch.


Method Dispatch

Dispatch determines which implementation of a method to call.

Static Dispatch

Chosen at compile time (e.g., function overloading):

void draw(Shape s);
void draw(Circle c);

Dynamic Dispatch (Single Dispatch)

Chosen at runtime based on the receiver object’s class:

Shape* s = new Circle();
s->area(); // Circle::area() called dynamically

Implementation detail:

  • Each class stores a vtable (virtual method table).

  • Objects carry a hidden pointer to their class’s vtable.

  • Method calls lookup entries in that table.

Example

Diagram idea (dispatch_mechanisms.svg)
Timeline:

  • Static dispatch resolved at compile time.

  • Dynamic dispatch resolved via vtable lookup at runtime.


Overriding and super

A subclass can override inherited methods to refine behavior:

class ColoredCircle extends Circle {
  String color;
  double area() {
    System.out.println("Computing area for " + color);
    return super.area(); // call parent method
  }
}

Rules vary by language:

  • Java/C#: require @Override annotation or equivalent.

  • C++: uses virtual keyword to mark dispatchable methods.

  • Python: all methods are virtual by default.


Encapsulation

Encapsulation restricts access to an object’s internal state through:

  • Visibility modifiers (private, protected, public)

  • Accessors and mutators (getters/setters)

  • Immutability or controlled mutation

This hides representation details and prevents direct external modification:

class BankAccount {
  private double balance;
  public void deposit(double x) { balance += x; }
  public double getBalance() { return balance; }
}

Warning

Violating encapsulation leads to tight coupling and fragile dependencies.


Multiple Dispatch

In single dispatch, method choice depends on one receiver’s type.
Multiple dispatch generalizes this to depend on all argument types.

Example (Julia):

area(x::Circle, y::Circle) = ...
area(x::Rectangle, y::Circle) = ...

Dispatching is based on the tuple of argument types.

Note

Multiple dispatch better supports symmetric operations (e.g., geometry, algebra), where no single argument should dominate.


Object Identity

Objects have identity, even if they contain the same data:

a = [1, 2]; b = [1, 2]
a == b    # true  (structural equality)
a is b    # false (distinct objects)

Identity allows mutable state tracking — essential for references and effects.


Common Pitfalls

Warning

  • Fragile base class problem: subclass changes invalidate parent assumptions.

  • Slicing: assigning derived objects to base by value (C++).

  • Inheritance abuse: using subclassing instead of composition.

  • Tight coupling: dependent modules break when hierarchy changes.


Type Systems and OOP

Languages differ in how they express object and class relationships:

LanguageType RelationDispatchNotes
JavaNominal subtypingSingleInterfaces as structural contracts
PythonDynamic structuralSingleDuck typing at runtime
C++NominalSingle or staticVirtual vs non-virtual
JuliaStructuralMultipleEfficient multimethods
ScalaNominal + structuralSingleTraits support composition

Note

Some modern systems (Rust, Go) avoid classes entirely but still provide method dispatch via traits/interfaces.


Design Perspective

Classes aren’t just technical — they define modularity boundaries:

  • Abstract data types (ADTs) evolved into objects.

  • OOP shifts from functions over datadata with functions attached.

  • Dispatch generalizes polymorphism to runtime decision-making.

Tip

When designing APIs, prefer composition first, inheritance only if necessary.
Interfaces and traits often provide cleaner, more flexible reuse.


Diagram Concepts

  • class_hierarchy_inheritance.svg: a visual of Shape → Circle → ColoredCircle with subtype arrows.

  • dispatch_mechanisms.svg: timeline comparing static vs dynamic lookup.

  • composition_vs_inheritance.svg: show the “has-a” vs “is-a” distinction clearly.


See also