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:
Shapedefines an interface;Circleprovides 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 (
Circleis aShape). -
Composition models a “has-a” relationship (
Carhas anEngine).
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 dynamicallyImplementation 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
@Overrideannotation or equivalent. -
C++: uses
virtualkeyword 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:
| Language | Type Relation | Dispatch | Notes |
|---|---|---|---|
| Java | Nominal subtyping | Single | Interfaces as structural contracts |
| Python | Dynamic structural | Single | Duck typing at runtime |
| C++ | Nominal | Single or static | Virtual vs non-virtual |
| Julia | Structural | Multiple | Efficient multimethods |
| Scala | Nominal + structural | Single | Traits 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 data → data 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.