Lectures
Lecture 1: The Essence of Objects
Lecture 2: Unions of Objects
Lecture 3: Classes of Objects:   Data Definitions
Lecture 4: Classes of Objects:   Interface Definitions
Lecture 5: Interface Design:   Independent and Extensible
Lecture 6: Parametric Interface Definitions and Methods
Lecture 7: Introducing Java:   Syntax and Semantics
Lecture 8: Union, Interfaces, and Lists in Java
Lecture 9: Testing in Java
Lecture 10: Parametric Interfaces in Java
Lecture 11: Computations on Many Structural Arguments:   Double Dispatch
Lecture 12: Parameterized Types and Double Dispatch; Abstracting Values
Lecture 13: Abstracting Computation with Function Objects
Lecture 14: Function Objects & Parameterized Types; Anonymous Classes & Lambda
Lecture 15: The Fundamental List Abstraction:   Fold
Lecture 17: Midterm Review
Lecture 16: Properties of Equality:   Reflexive, Symmetric, Transitive, and Total
Lecture 19: Structural Equality with Double Dispatch; Abstracting and Overridding
Lecture 18: More Double Dispatch
Lecture 22: Optional, Maps, Sets, and Lifting Default Code to Abstract Classes
Lecture 23: The Visitor Pattern
Lecture 24: Implementing Visitors; Bank Accounts
Lecture 25: Imperatives:   Implicit Communication via Side-Effects
Lecture 26: Aside:   List Exercises
Lecture 27: Imperatives:   Cyclic Data
Lecture 28: Imperatives:   Methods over Cylic Data
Lecture 29: BSTs, Maps, The Law of Hash  Code, and Comparable vs Comparators
Lecture 30: Random access and Array  Lists
Lecture 31: Implementing Hash Tables
Lecture 32: Resizing Hash Tables
Lecture 33: Simple Iterators
Lecture 34: List Iterators and Iterator Combinators
Lecture 35: List Iterators and Iterator Combinators
Lecture 36: Zippers
Lecture 37: Naive Tree Iterators
Lecture 38: Efficient Pre-Order Tree Iterators
Lecture 39: Drills
Lecture 40: Drill Solutions
Lecture 41: Wrap-up
6.12

Lecture 1: The Essence of Objects

Video 2019. Video 2018.

One of the key concepts behind so-called object-oriented programming (OOP) is the notion of an object. An object is a new kind of value that can, as a first cut, be understood as a pairing together of two familiar concepts: data and function.

This suggests that objects are a natural fit for well-designed programs since good programs are organized around data definitions and functions that operate over such data. An object, in essence, packages these two things together into a single programming apparatus. This has two important consequences:

  1. You already know how to design programs oriented around objects.

    Since objects are just the combination of two familiar concepts that you already use to design programs, you already know how to design programs around objects, even if you never heard the term “object” before. In short, the better you are at programming with functions, the better you will be at programming with objects.

  2. Objects enable new kinds of abstraction and composition.

    Although the combination of data and function may seem simple, objects enable new forms of abstraction and composition. That is, objects open up new approaches to the construction of computations. By studying these new approaches, we can distill new design principles. Because we understand objects are just the combination of data and function, we can understand how all of these principles apply in the familiar context of programming with functions. In short, the better you are at programming with objects, the better you will be at programming with functions.

Let’s start by looking at a very simple program developed using the systematic approach of last semster. This program operates on Cartesian coordinates and consists of a couple simple functions for computing the distance of a coordinate from the orgin and for moving a point by given offsets:

; A Coord is a (make-coord Real Real)
; Interp: Cartesian coordinate
(define-struct coord (x y))
 
; dist0 : Coord -> Real
; Distance of given coordinate to origin
(check-expect (dist0 (make-coord 3 4)) 5)
(define (dist0 c)
  (sqrt (+ (sqr (coord-x c))
           (sqr (coord-y c)))))
 
; move : Coord Real Real -> Coord
; Move given coordinate by offsets
(check-expect (move (make-coord 3 4) 2 -1) (make-coord 5 3))
(define (move c dx dy)
  (make-coord (+ (coord-x c) dx)
              (+ (coord-y c) dy)))

This program consists of two parts: a data definition for coordinates and a set of function definitions that operate on coordinates.

An object, is in essence, just the fusing together of those two parts. Here is a similar program developed using objects:

; A Coord is a (new coord% Real Real)
; Interp: Cartesian coordinate
 
(define-class coord%
  (fields x y)
 
  ; dist0 : -> Real
  ; Distance of this coordinate to origin
  (check-expect (send (new coord% 3 4) dist0) 5)
  (define (dist0)
    (sqrt (+ (sqr (send this x))
             (sqr (send this y)))))
 
  ; move : Real Real -> Coord
  ; Move this coordinate by offsets
  (check-expect (send (new coord% 3 4) move 2 -1) (new coord% 5 3))
  (define (move dx dy)
    (new coord%
         (+ (send this x) dx)
         (+ (send this y) dy))))

Notice that this programs consists of a single class definition. A class definition is a mechanism for defining a set of objects; similar to how a struct definition is a mechanism for defining a set of structures.

The coord% class defines a new kind of value, which are objects consisting of two fields x and y, and two methods dist0 and move.

To create an object that is an member of the coord% class, you use new, the name of the class (coord%), and the appropriate number (and kind) of values to place in the fields of the object. For example, (new coord% 3 4) will construct a Coord that represents a point at (3,4). This is analogous to using the make-coord constructor in the original program.

To access the data within an object, you use send, a Coord value, and the name of the field you’d like to extract. For example, (send (new coord% 3 4) x) will produce 3, which is the value of the x field within the object (new coord% 3 4). This is analogous to using the coord-x accessor function in the oroginal program.

To compute with an object, you use send, a Coord value, the name of the method you’d like to compute, and any additional arguments the method expects. For example, (send (new coord% 3 4) dist0) computes 5, the distance of (3,4) to the origin. Notice that this method takes no additional arguments; it is a function of just the data within the coordinate. On the other hand, (send (new coord% 3 4) move 1 2) takes two arguments: the change in x and y. It computes a new coordinate representing (4,6), namely (new coord% 4 6).

Notice that the definitions of dist0 and move make use of the variable this, which doesn’t seem to be defined anywhere. The name this is implicitly defined to me “the object on which the method was invoked.” So for example, when computing (send (new coord% 3 4) dist0), the name this means (new coord% 3 4) in the body of dist0.

Notice that there are subtle changes to the signature and purpose statement. Spend some time comparing them and try to formulate why these differences exist.