Object-Oriented Programming I

Table of Contents

1 Reading

For more examples of defining classes in Java, read Bailey 1.3–1.7 (p. 8–22) after watching the lecture/reading the notes. Section 1.6 (p. 18–20) in particular has good advice about how to think about designing a data structure object.

2 Video

Here is a video lecture for the material outlined below. If you prefer (or want supplemental) textbook readings, I would recommend Defining Classes in Java from the Java for Python Programmers book (this covers material from Friday's topic as well).

The video contains sections on

  1. Muffin analogy (0:15)
  2. Abstraction (1:07)
  3. Object-oriented programming: the concept (2:41)
  4. Interaction via method calls (6:05)
  5. Terminology: class (11:05)
  6. Terminology: object (12:45)
  7. Terminology: field (16:13)
  8. Terminology: method (18:17)
  9. Terminology: constructor (22:45)
  10. Terminology: accessor (27:55)
  11. Terminology: mutator (30:58)
  12. Return type void

The Panopto viewer has table of contents entries for these sections: https://carleton.hosted.panopto.com/Panopto/Pages/Viewer.aspx?id=ef062316-103e-4a60-a827-aca30166cc4c

3 Data Abstraction and Encapsulation

If you purchase a delicious lemon poppy-seed muffin at Brick Oven Bakery in Northfield, you can tell it's a muffin without knowing the ingredients. Muffins are cylindrical, breadlike, and sweet. The specific ingredients are not your concern. Brick Oven would be free to switch to a different sweetener or lemon flavoring as long as the taste of the muffin was maintained. The muffin's ingredients and construction are details that probably don't interest you.

In a similar fashion, we can separate the implementation of a data structure from how it is used.

  • For example, we are familiar with how strings and arrays can be used to solve problems (their semantics), but might not know the details of their inner workings.
    • Do all consecutive locations in an array appear close together in memory in your computer, or are they far apart?
    • In most cases, the answer is unimportant! As along as it behaves like an array, we're good to go
  • Put another way, we care about the abstraction of an array—a sequence of elements that can be accessed in any order—and don't care about how this abstraction is implemented*
    • ∗ As long as said implementation isn't grossly inefficient

4 The Object-Oriented Model

  • It is often useful to conceptualize a computer program as performing some series of manipulations on input data in order to produce some output data
  • In object-oriented programming (OOP), a program's data is managed by its objects
  • Each object manages its own data
    • A point on the screen has two coordinates
    • A medical record maintains a name, list of dependents, medical history, insurance information
    • A strand of genetic material has a sequence of base pairs
    • We will refer to the particular arrangement and values of an object's data at a point in time as the object's state
  • But how does a program interact with its objects and the data inside them?
    • Through messages or method calls to the objects
    • A string might receive a message "tell me your length," while a medical record might receive a "change insurance" message
      • The former accesses data while the latter modifies it
      • This modification to the medical record object might involve changing several pieces of data in that and other objects in a consistent manner
        • For example, if we directly change the reference to the insurance company, we might forget to change similar references in each of the dependents
        • We don't want the programmer to have to remember to perform all the individual steps to move a complex data structure from one state to another
    • Thus, we have the designer of the medical record object provide a method for making this change, and our program only has to send the high-level message "change insurance"

5 Object-Oriented Terminology

  • Class
    • A definition of what it means to be a particular kind of object
    • Includes both data and behavior
    • For example, a rectangle class would have a height and width (data) and the ability to calculate its area (behavior)
  • Object
    • An instance of a class
    • We can have many instances of the same class
      • Typically each with its own data, but all sharing the same behavior
    • For example, a program could create a bunch of different rectangles with different heights and widths
      • The formula (and therefore the code) to calculate the area would be the same for all
    • Demonstration of encapsulation: a single rectangle object groups together (encapsulates) the various data needed to define the object
      • As opposed to having to create separate width and height variables for each rectangle our program wants to represent
  • Field
    • A variable associated with a class or object
    • The class definition defines what fields instances of that class (objects) have
    • Like all variables in Java, fields are declared to have a certain type
    • width and height would each be fields of a rectangle object, with a numerical type such as double
      class Rectangle {
          double width;
          double height;
      
          /* method implementations
           * go here
           */
          // /* ... */ is the syntax for a multi-line comment in Java
      }
      
  • Method
    • A functions associated with a class or object
    • The class definition defines the methods for that class and objects that are instances of it
    • Just as variables are declared to have a type, methods declare the types of any parameters as well as the type of return value
      • Continuing our rectangle example, the method for computing the area might look like this:
        // double is the return type
        double getArea() {  // curly braces show where the method body begins and ends
            return width * height;
        }
        
    • A program calls the methods of an object to interact with it
      • So if rect is a variable of type Rectangle, we could write the following to print the area:
        System.out.println("Area = " + rect.getArea());
        
  • Constructor
    • A special type of method that is called whenever a new object of that class is created
    • Responsible for initializing (giving values to) all the object's fields, putting it in a predictable and consistent initial state
      • The constructor typically has parameters specifying some or all of these initial values
    • Constructors always have the same name as the class
    • Our Rectangle constructor might look like this:
      Rectangle(double w, double h) {
          width = w;
          height = h;
      }
      
  • Accessor (a.k.a. getter)
    • A method that returns (or gets) object data without modifying anything about the object
    • In Java it's best practice to always retrieve object data via methods and never by accessing fields directly
      • So it's typical to see a getX method for each field X
      • Our Rectangle would have accessors for the width and height fields:
        double getWidth() {
            return width;
        }
        
        double getHeight() {
            return height;
        }
        
  • Mutator (a.k.a. setter)
    • A method that modifies (or sets) object data (the counterpart to accessors)
    • Again, good object design in Java means making methods the way code interacts with objects
      • So it's also typical to see setX methods
      • If we wanted to make our Rectangle class mutable (meaning its state can change after creation) we could create accessors for the width and height fields:
        void setWidth(double w) {
            width = w;
        }
        
        void setHeight(double h) {
            height = h;
        }
        
      • Note how we have to declare the types of the parameters
      • When a method doesn't return anything (like these ones), the return type must be declared as void
        • Now where have we seen that before… (main!)
  • Using getters and setters instead of directly manipulating object fields helps preserve the abstraction that is a major goal of object-oriented programming
    • It allows the underlying data representation to change without requiring all code using the object to also be changed

Here's the Rectangle class in full:

class Rectangle {
    // fields
    double width;
    double height;

    // constructor
    Rectangle(double w, double h) {
        width = w;
        height = h;
    }

    // accessor methods
    double getWidth() {
        return width;
    }

    double getHeight() {
        return height;
    }

    double getArea() {
        return width * height;
    }

    // mutator methods (don't return anything, so return types are void)
    void setWidth(double w) {
        width = w;
    }

    void setHeight(double h) {
        height = h;
    }

    // main method
    public static void main(String[] args) {
        // use the new keyword along with a call to the constructor to create a new object
        Rectangle r = new Rectangle(5, 5);
        System.out.println("Area = " + r.getArea());
    }
}

6 Practice Problems1

  1. What is the difference between an object and a class?
  2. What is the difference between an accessor and a mutator?
  3. Consider this simple BankAccount class:
    /**
     * An overly simplified implementation of a bank account
     * @author, 2001 duane a. bailey
     */
    class BankAccount
    {
        String account;  // the account number
        double balance; // the balance associated with account
    
        /**
         * @pre account is a string identifying the bank account
         * balance is the starting balance
         * @post constructs a bank account with desired balance
         */
        BankAccount(String acc, double bal)
        {
            account = acc;
            balance = bal;
        }
    
        /**
         * Account accessor.
         * @post returns the bank account number of this account
         */
        String getAccount()
        {
            return account;
        }
    
        /**
         * Balance accessor.
         * @post returns the balance of this bank account
         */
        double getBalance()
        {
            return balance;
        }
    
        /**
         * Deposit mutator.
         * @post deposit money in the bank account
         */
        void deposit(double amount)
        {
            balance = balance + amount;
        }
    
        /**
         * Withdrawal mutator.
         * @pre there are sufficient funds in the account
         * @post withdraw money from the bank account
         */
        void withdraw(double amount)
        {
            balance = balance - amount;
        } 
    }
    

    Which of the following variables are associated with valid constructor calls?

    BankAccount a,b,c,d,e,f;
    a = new BankAccount("1",300.0);
    b = new BankAccount(300.0,"Bob");
    c = new BankAccount(033414,300.0);
    d = new BankAccount("789",300);
    e = new BankAccount("000700600",new Double(300));
    f = new BankAccount("Bob",(double)300);
    
  4. Suppose we modify this BankAccount class to have a method inside it, defined as:
    double computeInterest(double rate)
    

    If the client code (i.e., someone else's code using our BankAccount class) has declared a BankAccount variable named acct, which of the following would be a valid call to the above method?

    1. double result = computeInterest(acct, 0.42);
    2. acct.computeInterest(42.0, 0.15);
    3. int result = BankAccount.computeInterest(0.42);
    4. double result = acct.computeInterest(0.42);
    5. new BankAccount(100).computeInterest();
  5. Design a class Point to represent a 2D point with an x- and y-coordinate. Implement a method
    double distance(Point other)
    

    that returns the distance between the current Point object and the given other Point object. The distance between two points is equal to the square root of the sum of the squares of the differences of their x- and y-coordinates. In other words, the distance between two points \((x_1,y_1)\) and \((x_2,y_2)\) can be expressed as \(\sqrt{(x_2 - x_1)^2 + (y_2 - y_1)^2}\).

7 Learning Block

  • Questions?
  • Brief office tour
  • Get to know you, one-two sentence review of a movie, show, song, or book you consumed recently
  • Form question
  • Lab 0 out
  • Reminder: surveys

Footnotes:

1

Solutions:

  1. A class provides the details of an implementation of a data structure. An object is a particular instance (or, sometimes, an instantiation) of a class. Objects take up memory; classes are simply models for objects. A class need not be instantiated. java.lang.Math is an example of a instance-free class.
  2. An accessor provides read-only access to an implementation's data, directly or indirectly. It does not modify the structure. A mutator allows the user to modify the state of an object. It may also return a structure's data.
  3. a, d, e, and f are valid. d is okay even though 300 is passed as an integer—Java will automatically convert it to a double in this case. f is a valid call, though it does not seem to follow the documented purpose of the first argument (i.e., ~"Bob"~ is an unlikely account number).
  4. 4. double result = acct.computeInterest(0.42);
  5. class Point {
        double x;
        double y;
    
        Point(x, y) {
            this.x = x;
            this.y = y;
        }
    
        double getX() {
            return x;
        }
    
        double getY() {
            return y;
        }
    
        double distance(Point other) {
            double xDiffSq = Math.pow(other.getX() - this.getX(), 2);
            double yDiffSq = Math.pow(other.getY() - this.getY(), 2);
            return Math.sqrt(xDiffSq + yDiffSq)
        }