Chapter 11 – Abstract Data Types and Encapsulation Constructs
Core idea: Data abstraction = hide how data is represented,
expose only operations. ADTs and encapsulation are language mechanisms to support this.
11.1 The Concept of Abstraction
Abstraction = ignoring irrelevant details and focusing on the
essential properties of an entity or operation.
Two main kinds of abstraction:
- Control abstraction – implemented using subprograms (functions, procedures).
- You call
sort(a) without caring whether it uses quicksort, mergesort, etc.
- Hides how the computation is done; you only care about what it does.
- Data abstraction – implemented using abstract data types.
- You work with a
Stack using push/pop without knowing if it uses an array or linked list.
- Hides internal representation and focuses on allowed operations.
11.2 Introduction to Data Abstraction
Data abstraction means that a data type is defined by:
- the set of values it can hold
- the set of operations that can be performed on it
- NOT by its underlying representation
An Abstract Data Type (ADT) is a data type where:
- Representation is hidden.
- Access is only via a well-defined interface (operations/methods).
Benefits of data abstraction:
- Modularity – large programs split into well-defined components.
- Information hiding – clients can’t accidentally corrupt internals.
- Maintainability – representation can change without changing client code.
- Reusability – ADTs can be reused in different programs.
- Reduced complexity – clients think in terms of “operations”, not low-level details.
11.3 Design Issues for Abstract Data Types
Key questions language designers must answer about ADTs:
- Definition form: How is an ADT declared? (class, module, package, etc.)
- Access control: How do we specify public vs private components?
- Storage management:
- How are instances created/destroyed?
- Manual deallocation (
delete) vs. garbage collection?
- Initialization: Constructors? Initialization rules? Default values?
- Type checking: Is the ADT usage checked at compile time?
- Operations binding: How are operations bound to the type? (methods, operators)
11.4 Language Examples (ADT Support)
C++
Uses class as an ADT mechanism.
- Access control:
private, public, protected.
- Constructors and destructors for initialization/cleanup.
- Operator overloading for intuitive syntax.
- Manual memory management (flexible but error-prone).
// Example C++ ADT
class Stack {
private:
int* data;
int topIndex;
public:
Stack(int capacity);
~Stack();
void push(int value);
int pop();
bool isEmpty() const;
};
Java
- Everything (except primitives) is a class → classes = ADTs.
- Access control:
private, public, protected, package-access.
- No pointer arithmetic → safer encapsulation.
interface types allow pure behavioral (operation-only) abstraction.
- Automatic garbage collection.
C#
- Very similar to Java’s model.
- Includes
struct (value types) as lightweight ADTs.
- Properties (
get/set) provide encapsulated access to fields.
Ruby
- Everything is an object; classes define ADT-like behavior.
- Dynamic typing, flexible but less compile-time checking.
- Access control:
public, protected, private for methods.
- Mixins via modules instead of classic multiple inheritance.
Python
- Classes implement ADTs; dynamic typing.
- “Private” fields via name mangling (
__field) and conventions (leading underscore).
- Properties via
@property for controlled attribute access.
- Privacy is mostly by convention (“consenting adults”).
11.5 Parameterized Abstract Data Types (Generics)
Parameterized ADT = ADT that takes a type parameter.
Examples:
List<T>, Stack<T>, Map<K, V>.
Different implementations:
- C++ templates – compile-time generics, very powerful, sometimes complex errors.
- Java generics – implemented with type erasure (generic info removed at runtime).
- C# generics – reified generics (type info kept at runtime), safer than Java’s model.
Benefits: reusability, type safety, no need for “void*” or casts, fewer runtime errors.
11.6 Encapsulation Constructs
Encapsulation = grouping related declarations and hiding implementation details.
- Modules, packages, namespaces, classes.
- Control visibility of:
- types, variables, functions/subprograms
- constants, helper functions
Examples:
- C++: headers + source files, namespaces.
- Java: packages (
package com.example;), access levels.
- C#: namespaces, assemblies.
- Python: modules and packages (
import).
11.7 Naming Encapsulations
This covers how encapsulated units (modules, packages, namespaces) are named and referenced.
- Names determine where declarations are visible (scope).
- Helps avoid name collisions in large programs.
- Supports separate compilation and linking.
Examples:
- Java packages = directory structure +
import.
- C# namespaces = logical grouping inside assemblies.
- Python modules = file names, imported with
import module.
Chapter 12 – Support for Object-Oriented Programming
Core idea: OOP = ADTs + inheritance + dynamic binding.
This chapter explains what “object-oriented” really means in language design.
12.1 Introduction
OOP is built on the foundations of data abstraction (Chapter 11), plus:
- Inheritance – reuse and extend existing types.
- Dynamic binding – method version chosen at runtime based on the actual object.
- Polymorphism – same operation name behaves differently on different types.
Languages differ in how “purely” object-oriented they are:
- Hybrid: C++, Objective-C.
- More pure: Smalltalk, Ruby.
- Mainstream OOP: Java, C#.
12.2 Object-Oriented Programming
A language is typically considered object-oriented if it supports:
- Abstract Data Types / Classes – bundle data + operations.
- Inheritance – new classes can be defined from existing ones.
- Dynamic binding – method calls bound at runtime, enabling polymorphism.
12.2.2 Inheritance
Inheritance allows a new class (subclass) to reuse and extend an existing class (superclass).
- Superclass / base / parent class.
- Subclass / derived / child class.
Motivations for inheritance:
- Software reuse – don’t rewrite similar code.
- Natural hierarchy – real-world entities often form hierarchies.
- Organizing large codebases.
Inherited members can be:
- Used as-is.
- Overridden (redefined) in the subclass.
- Extended with new methods/fields.
12.3 Design Issues for Object-Oriented Languages
Key design concerns include:
- Object creation & destruction:
- Constructors, destructors, garbage collection.
- Inheritance model:
- Single inheritance vs multiple inheritance.
- Method overriding & access control:
- Keywords like
virtual, override, final.
public/private/protected rules.
- Dynamic binding rules:
- Are all methods dynamically bound, or only some (like in C++)?
- Type checking:
- Static vs dynamic typing.
12.4 OOP in Specific Languages
Smalltalk
- Everything is an object (even numbers, booleans).
- All operations are message sends.
- All binding is dynamic.
- One of the “purest” OOP languages.
C++
- Hybrid: supports both procedural and OOP styles.
- Supports multiple inheritance.
- Manual memory management.
- Static typing, some dynamic features (RTTI).
Objective-C
- Adds OOP features to C.
- Uses message passing similar to Smalltalk.
- Uses protocols (like interfaces) for behavioral abstraction.
Java
- Designed primarily for OOP (though primitives are not objects).
- Single inheritance of classes; multiple inheritance via interfaces.
- Automatic garbage collection.
- Strong static typing.
C#
- Very similar to Java’s OOP model.
- Supports structs (value types) in addition to classes.
- Properties, events, delegates.
Ruby
- Dynamic, pure OOP (everything is an object).
- Open classes and mixins (modules) instead of multiple inheritance.
- Flexible but less static checking.
12.5 Implementation of OOP Constructs
How languages implement OOP features internally:
- Object layout – objects store:
- instance variables
- a reference to class metadata (for methods, type information)
- Dynamic binding – often implemented using virtual method tables (vtables).
- Each class has a table of method pointers.
- Objects point to their class’s vtable.
- Method call → index into vtable at runtime → correct method invoked.
- Inheritance affects memory layout and vtable composition.
12.6 Reflection
Reflection = a program can inspect or modify its own structure at runtime.
Typical capabilities:
- List methods and fields of a class.
- Create objects of a class given its name as a string.
- Dynamically call a method by name.
Supported in:
- Java (
java.lang.reflect).
- C# (
System.Reflection).
- Dynamic languages (Ruby, Python) with strong introspection capabilities.
Chapter 14 – Exception Handling and Event Handling
Core idea:
Exception handling = reacting to unusual conditions (errors or not) in a structured way.
Event handling = reacting to external events (usually GUI user actions).
14.1 Introduction to Exception Handling
Exception = any unusual event (error or not) that is detectable
by hardware or software and may require special processing.
Examples:
- Floating-point overflow, division by zero.
- Array index out of bounds.
- End-of-file condition.
- File not found, invalid input.
Exception handler = code segment executed when an exception occurs.
Raising (or throwing) an exception = signaling that an exceptional condition has occurred.
Without built-in exception handling
Older approaches:
- Return status codes (error flags) and check them manually.
- Use label parameters (Fortran) to jump to error-handling code.
- Pass error-handling subprograms as parameters.
Problems: code gets cluttered, logic becomes messy, error-handling is inconsistent.
Advantages of built-in exception handling
- Cleaner source code.
- Compiler/runtime can automatically insert and manage checks.
- Exception propagation:
- Exception automatically searches for a suitable handler in calling units.
- Lower-level code doesn’t have to handle every error itself.
14.2 Exception Handling in C++
C++ supports exceptions with:
try { ... } – block of code being watched.
catch(Type x) { ... } – handlers.
throw expr; – raise an exception.
Issues:
- Any type can be thrown (int, float, object) → semantically messy.
- Exception specifications (
throw(int, float)) are mostly ignored by compilers.
- No
finally (cleanup must be manual or via RAII).
14.3 Exception Handling in Java
Java has a more disciplined exception model.
Hierarchy
Throwable
Error – serious system errors, usually not handled by applications.
Exception
RuntimeException and subclasses – unchecked exceptions.
- Other subclasses – checked exceptions.
Syntax
try {
// code that may throw
} catch (SomeException e) {
// handler
} finally {
// code that always runs
}
Checked vs unchecked exceptions:
- Checked – must be declared in
throws or handled.
- Unchecked – subclasses of
RuntimeException.
- e.g.
NullPointerException, ArrayIndexOutOfBoundsException.
finally is executed regardless of how the try block exits, used for cleanup.
Assertions in Java (14.3.7)
Used for defensive programming.
assert condition;
assert condition : "message";
If the condition is false → AssertionError is thrown. Assertions can be enabled/disabled at runtime with JVM flags.
Evaluation of Java’s design (14.3.8)
- Only
Throwable objects can be thrown → clearer semantics.
- Compiler enforces checked exceptions via
throws.
finally block is very useful for cleanup.
- Java runtime implicitly throws many useful exceptions (null deref, bounds, etc.).
- C# is similar, but without checked exceptions (
throws clause).
14.4 Exception Handling in Python and Ruby
Python (14.4.1)
In Python, exceptions are objects.
- Base classes:
BaseException → Exception.
- Subclass examples:
OverflowError, ZeroDivisionError, FloatingPointError.
IndexError, KeyError.
try:
# code
except Exception1:
# handler
except Exception2:
# handler
else:
# runs if no exception
finally:
# always runs
Differences from Java:
- Uses
except instead of catch.
else clause runs only if no exception is raised.
finally works similarly (cleanup).
- No
throws equivalent → no checked exceptions.
raise is used to trigger an exception:
raise IndexError
raise ValueError("bad value")
assert is a conditional raise of AssertionError and can be disabled with the -O flag.
Ruby (14.4.2)
- Exceptions are objects; most application exceptions descend from
StandardError (which extends Exception).
- Each exception has:
message → error text.
backtrace → stack trace.
Raising exceptions:
raise "bad parameter" if count == 0
raise TypeError, "Float parameter expected" if !param.is_a?(Float)
Handling exceptions:
begin
# code
rescue
# handler
else
# if no exception
ensure
# always runs (like finally)
end
Ruby allows retry at the end of a handler to re-run the protected code.
14.5 Introduction to Event Handling
Event = notification that something external has occurred (e.g. GUI interaction).
Event handler = code that runs when an event occurs.
Event-driven programming:
- Program flow is driven by events rather than a fixed sequence of calls.
- Common in GUIs and web browsers.
Examples:
- Button clicks.
- Text field changes.
- Form submissions.
14.6 Event Handling with Java
Java uses Swing components and listener interfaces.
GUI Elements (Widgets)
JTextField – text input box.
JRadioButton – radio buttons grouped with ButtonGroup.
Event Listeners
- Interfaces like
ActionListener, ItemListener, etc.
- To handle an event:
14.7 Event Handling in C#
C# uses .NET’s Windows Forms and the event/delegate model.
Delegates are type-safe function pointers that define the protocol for event handlers.