Here are the answers to homework 5.

Assignment 5: Classes and Objects Revisited
Solutions

The answers are in blue.

Warm-ups:

1. Consider the following 'nonsense class'.

class A
{ public A() { n = 0; } // constructor 
  public A(int a) { n = a; } // constructor  
  public void f() { n++; } // mutates n  
  public void g() { f(); n = 2 * n; f(); } 
  // g mutates n directly and indirectly (f) 
  public int h() { return n; } // accessor of n  
  public void k() { System.out.println(n); } 
  // does not return, only prints, the value of n 
  private int n; // instance variable  
}
Identify the constructors, mutator functions, and accessor functions. What kind of variable is n?


2. With the nonsense class from the preceding exercise, determine what the following program prints.

It prints 9 and 17 and here's why:

public static void main(String[] args) {
  A a = new A(); // a.n is 0 
  A b = new A(2); // b.n is 2 
  A c = b; // c points where b points so c.n = b.n = 2 
  A d = new A(3); // d.n is 3  
  a.f(); // a.n is 1  
  b.g(); // b.n becomes 3, 6, then 7 
  c.f(); // c.n is b.n and it becomes 8 
  d.g(); // d.n becomes 4, 8, then 9 
  d.k(); // d.n is printed on a line by itself 
  A e = new A(a.h() // a.n is 1  
            + b.h() // b.n is 8 and is equal to c.n   
            + c.h() // which is 8 
        ); // so e.n is set to 1 + 8 + 8 = 17 
  e.k(); // e.n (17) is printed on a line by itself   
}
Work through the program by hand. Do not actually compile and run the program. Then run it and compare the results.


3. Implement all the methods of the following class:

class Person {
  public Person() {
   name = "YetToBeNamed";
   birthdayYear = 1999; // my default  
  }
  public Person(String givenName, int yearOfBirth) { 
    name = givenName;
    birthdayYear = yearOfBirth; 
  }
  public String getName() { 
    return name; 
  }
  public String changeName(String name) { 
    String aux; 
    aux = this.name;
    this.name = name;
    return aux; 
  }
  public int getAgeInYears(int currentYear) { 
    return currentYear - birthdayYear;  
  }
  private String name;
  private int birthdayYear; 
  public static void main(String[] args) {
    Person a = new Person(); 
    Person b = new Person("Richard P. Feynman", 1918); 
    String name = a.changeName("The Next Richard Feynman");     
    System.out.println(
      "Physicist " + name + " makes big " + 
      "discovery, touted as " + a.getName()    
    );
    System.out.println(
       b.getName() + " was " + 
       b.getAgeInYears(1945) + 
       " in 1945, in May. "
    );
  }  
}
Write a small test program that creates and works with objects of class Person as well.

As shown, the test program can be the very main method of the class we wrote.


Design exercises:

4. Implement a class Address. An address has

Supply two constructors: Supply a print function that prints the address with the street on one line and the city, state, and postal code on the next line.

Supply a method compareTo that tests whether one address comes before another when the addresses are compared by postal code.

class Address {
  int    houseNumber;
  String street;
  int    apartmentNumber;
  String city;
  String state;
  String zipCode; // e.g., "47405-1234"
  Address(int houseNumber, 
          String street, 
          // no apartmentNumber  
          String city, 
          String state, 
          String zipCode) { 
    this.houseNumber = houseNumber; 
    this.street = street; 
    this.city = city; 
    this.state = state; 
    this.zipCode = zipCode; 
  } 
  Address(int houseNumber, 
          String street, 
          int apartmentNumber, 
          String city, 
          String state, 
          String zipCode) { 
    this(houseNumber, street, city, state, zipCode);
    this.apartmentNumber = apartmentNumber; 
  } 
  void print() {
    System.out.println("Street: "      + street); 
    System.out.println("City: "        + city 
                     + "State: "       + state 
                     + "Postal Code: " + zipCode); 
  } 
  int compareTo(Address a) { 
    // same conventions as for Strings
    return this.zipCode.compareTo(a.zipCode); 
  } 
}


5. Implement a class Account. An account has

Pass a value into a constructor to set an initial balance.

If no value is passed the initial balance should be set to $0.

Charge a $5 penalty if an attempt is made to withdraw more money than available in the account.

Enhance the Account class to compute interest on the current balance.

class Account {
  double balance;
  double add(double sum) {
    balance += sum;
    return sum; 
  }
  double withdraw(double sum) {
    if (sum > balance) { 
      balance -= 5;
      return -5; // this will come in handy in Prob. 6 
    } else { 
      balance -= sum;
      return balance; // Notice: always >= 0 (never < 0)
    }
  }
  double inquire() { return balance; } 
  Account() { balance = 0; }
  Account(double sum) { balance = sum; }
  double interest (double rate) {
    return rate * balance; 
  }
}


6. Implement a class Bank. This bank has two objects

of the type Account that was developed in the preceding exercise.

Implement four instance methods:

deposit(double amount, String account)

withdraw(double amount, String account)

transfer(double amount, String account) 

printBalances()
Here the account string is "S" or "C". For the deposit or withdrawal, it indicates which account is affected. For a transfer it indicates the account from which the money is taken; the money is automatically transferred to the other account.

class Bank {
  Account checking;
  Account savings; 
  void deposit(double amount, String account) {
    if (account.equals("C")) checking.add(amount); 
    else // my default
      savings.add(amount); 
  }
  void withdraw(double amount, String account) {
    if (account.equals("C")) checking.withdraw(amount); 
    else // my default
      savings.withdraw(amount); 
  }
  void transfer (double amount, String account) {
    if (account.equals("C")) 
      if (checking.withdraw(amount) >= 0) 
        savings.add(amount); 
      else checking.add(5); // somewhat fault-tolerant
    else // default
      if (savings.withdraw(amount) >= 0) 
        checking.add(amount); 
      else savings.add(5);  // no penalty for transfers
  }
  void printBalances() {
    System.out.println(
      "Checking: " + checking.inquire() +
      "\nSavings: " + savings.inquire()
    ); 
  }
}


7. Define a class Country that stores the name of the country, its population, and its area. Using that class, write a test program that creates a few countries and stores them in an array and then prints

Here's an answer with the main as part of the very class we are defining:

class Country {
  String name;
  long population;
  double area;
  Country(String name, long population, double area) {
    this.name = name; 
    this.population = population; 
    this.area = area; 
  }
  double density () {
    if (area > 0) 
      return population/area; 
    else return 0; 
  }
  public static void main(String[] args) {
    int numberOfCountries = 10;
    Country[] countries = new Country[numberOfCountries];

    double lArea    = -1;    int lAreaIndex = -1; 
    long   lPop     = -1;     int lPopIndex = -1; 
    double lDensity = -1; int lDensityIndex = -1; 

    for (int i=0; i < countries.length; i++) {
      if (lArea < countries[i].area) {
        lArea = countries[i].area; 
        lAreaIndex = i; 
      } 
      if (lPop < countries[i].population) {
        lPop = countries[i].population; 
        lPopIndex = i; 
      } 
      if (lDensity < countries[i].density()) {
        lDensity = countries[i].density(); 
        lDensityIndex = i; 
      } 
    } 
    System.out.println(
      "Largest area: "       + country[lAreaIndex]       + 
      "Largest population: " + country[lPopulationIndex] + 
      "Largest density: "    + country[lDensityIndex]     
    ); 
  }
  String toString() { // comes in handy in the println above
    return "\nCountry name: " + name + 
           "\nPopulation: "   + population + 
           "\nArea: "         + area + 
           "\nDensity: "      + density() 
  } 
}


8. Write a class called Triangle that can be used to represent a triangle. It should include the following methods that return boolean values indicating if the particular property holds:

Write a simple tester program that creates a few triangles and asks them about their type.
class Triangle {
  int sideA, sideB, sideC; 

  Triangle (int a, int b, int c) { // constructor 
    sideA = a; sideB = b; sideC = c; 
  } // negative numbers and zero lengths accepted 
    // but see how isTriangle() used below

  boolean isTriangle () {
    int max = Math.max(Math.max(sideA, sideB), sideC); 
    return sideA > 0 && sideB > 0 && sideC > 0 &&
           (max < sideA + sideB + sideC - max); 
  }
  boolean isRight() {
    int max = Math.max(Math.max(sideA, sideB), sideC);     
    return isTriangle() &&
           (max * max) == (sideA * sideA) + 
                          (sideB * sideB) + 
                          (sideC * sideC) - 
                          (max * max);
  } 
  boolean isScalene() {
    return isTriangle() &&
           ! isIsosceles(); // we get some peer help 
  }
  boolean isIsosceles() {
    return isTriangle() &&
           (sideA == sideB || 
            sideA == sideC || 
            sideB == sideC); 
  }   
  boolean isEquilateral() {
    return isTriangle() && 
           (sideA == sideB && sideA == sideC); 
  }
  public static void main(String[] args) {
    Triangle a, b, c; 
    a = new Triangle( 3,  4,  5); 
    b = new Triangle( 0,  0, -1); 
    c = new Triangle( 6,  6,  6); 
    System.out.println(
      a + " isosceles? Answer: " + a.isIsosceles()
    );     
    System.out.println(
      a + " right? Answer: "     + a.isRight()
    );     
    System.out.println(
      c + " isosceles? Answer: " + c.isIsosceles()
    );     
    System.out.println(
      c + " scalene? Answer: "   + c.isScalene()
    );     
  } 
  String toString() {
    System.out.println(
      "Triangle: (" + sideA + ", " 
                    + sideB + ", " 
                    + sideC + 
                ") ";  
    ); 
  } 
}


9. This problem has several parts:

  1. Write a simple Vehicle class that has fields for (at least) current speed, current direction in degrees, and owner name.

    class Vehicle {
      int currentSpeed;
      int currentDirection; 
      String owner; 
    }
  2. Add a static field to your Vehicle class for the highest Vehicle Identification Number issued, and a non-static field that holds each vehicle's ID number.

    class Vehicle {
      int currentSpeed;
      int currentDirection; 
      String owner; 
      static int highestVIN; 
      int VIN;
    }
  3. Write a main method for your Vehicle class that creates a few vehicles and prints out their field values. Note that you can also write a separate tester program as well.

    class Vehicle {
    
      int currentSpeed;
      int currentDirection; 
      String owner; 
      static int highestVIN; 
      int VIN;
    
      public static void main(String[] args) {
    
        Vehicle a = new Vehicle(); 
        a.VIN = highestVIN++; 
        a.owner = "Larry Bird"; 
    
        Vehicle b = new Vehicle();
        b.VIN = highestVIN++; 
        b.owner = "Mark Jackson";     
    
        System.out.println("VIN: " + a.VIN + " belongs to " + a.owner); 
    
        System.out.println("VIN: " + b.VIN + " belongs to " + b.owner); 
    
        // Note that both cars are stopped, and facing East. 
    
      } 
    }
  4. Add two constructors to Vehicle. A no-arg constructor and one that takes an initial owner's name. Modify the tester program from the previous step and test your design.

    class Vehicle {
      int currentSpeed;
      int currentDirection; 
      String owner; 
      static int highestVIN; 
      int VIN;
    
      Vehicle() { VIN = highestVIN++; }
      Vehicle (String owner) { this(); this.owner = owner; } 
    
      public static void main(String[] args) {
    
        Vehicle a = new Vehicle("Larry Bird"); 
        Vehicle b = new Vehicle("Mark Jackson"); 
    
        System.out.println("VIN: " + a.VIN + " belongs to " + a.owner); 
        System.out.println("VIN: " + b.VIN + " belongs to " + b.owner); 
      }
    }
  5. Make the fields in your Vehicle class private, and add accessor methods for the fields. Which fields should have methods to change them and which should not?

    class Vehicle {
      private int currentSpeed;
      int getSpeed() {
        return currentSpeed; 
      }
      void changeSpeed(int newSpeed) {
        currentSpeed = newSpeed; 
      }
      private int currentDirection; 
      int getCurrentDirection() {
        return currentDirection; 
      }
      void setDirection(int newDirection) {
        currentDirection = newDirection % 360; 
      }
      private String owner; 
      int getOwner() {
        return owner; 
      }
      void setOwner(String newOwner) {
        owner = newOwner; 
      }  
      private static int highestVIN; 
      private int VIN;
      int getVIN() {
        return VIN; 
      } 
      Vehicle() { VIN = highestVIN++; }
      Vehicle (String owner) { this(); this.owner = owner; }
      public static void main(String[] args) {
        Vehicle a = new Vehicle("Larry Bird"); 
        Vehicle b = new Vehicle("Mark Jackson");
        System.out.println("VIN: " + a.getVIN() + " belongs to " + a.getOwner()); 
        System.out.println("VIN: " + b.getVIN() + " belongs to " + b.getOwner()); 
      }
    }
  6. Add a changeSpeed method that changes the current speed of the vehicle to a passed-in value, and a stop method that sets the speed to zero.

    class Vehicle {
      private int currentSpeed;
      int getSpeed() { return currentSpeed; }
      void changeSpeed(int newSpeed) { currentSpeed = newSpeed; }
      void stop() { currentSpeed = 0; }
      private int currentDirection; 
      int getCurrentDirection() { return currentDirection; }
      void setDirection(int newDirection) { currentDirection = newDirection % 360; }  
      private String owner; 
      int getOwner() { return owner; }
      void setOwner(String newOwner) { owner = newOwner; }
      private static int highestVIN; 
      private int VIN;
      Vehicle() { VIN = highestVIN++; }
      Vehicle (String owner) { this(); this.owner = owner; }
      public static void main(String[] args) {
        // ... 
      }
    }
  7. Add two turn methods to Vehicle. One that takes a number of degrees to turn, and one that takes simply either a Vehicle.TURN_LEFT or a Vehicle.TURN_RIGHT constant. Define the two constants accordingly.

    class Vehicle {
      private int currentSpeed;
      int getSpeed() { return currentSpeed; }
      void changeSpeed(int newSpeed) { currentSpeed = newSpeed; }
      void stop() { currentSpeed = 0; }
      private int currentDirection; 
      int getCurrentDirection() { return currentDirection; }
      void setDirection(int newDirection) { currentDirection = newDirection % 360; }
      static final String TURN_LEFT = "Turn left"; 
      static final String TURN_LEFT = "Turn left"; 
      void turn(int degrees) {
        currentDirection = (currentDirection + degrees) % 360; 
      } 
      void turn(String direction) {
        if (direction.equals(left)) {
          currentDirection = (currentDirection + 90) % 360; 
        } else { //default
          currentDirection = (currentDirection - 90) % 360; 
        } 
      } 
      private String owner; 
      int getOwner() { return owner; }
      void setOwner(String newOwner) { owner = newOwner; }
      private static int highestVIN; 
      private int VIN;
      Vehicle() { VIN = highestVIN++; }
      Vehicle (String owner) { this(); this.owner = owner; }
      public static void main(String[] args) {
        // ... 
      }
    }
  8. Add a static method to Vehicle that returns the highest identification number used so far.

    class Vehicle {
      private int currentSpeed;
      int getSpeed() { return currentSpeed; }
      void changeSpeed(int newSpeed) { currentSpeed = newSpeed; }
      void stop() { currentSpeed = 0; }
      private int currentDirection; 
      int getCurrentDirection() { return currentDirection; }
      void setDirection(int newDirection) { currentDirection = newDirection % 360; }
      static final String TURN_LEFT = "Turn left"; 
      static final String TURN_LEFT = "Turn left"; 
      void turn(int degrees) { currentDirection = (currentDirection + degrees) % 360; } 
      void turn(String direction) {
        if (direction.equals(left)) { currentDirection = (currentDirection + 90) % 360; } 
        else { currentDirection = (currentDirection - 90) % 360; } 
      }
      private String owner; 
      int getOwner() { return owner; }
      void setOwner(String newOwner) { owner = newOwner; }
      private static int highestVIN; 
      static int highestVIN() {
        return highestVIN;
      } 
      private int VIN;
      Vehicle() { VIN = highestVIN++; }
      Vehicle (String owner) { this(); this.owner = owner; }
      public static void main(String[] args) {
        // ... 
      }
    }
  9. Add a toString method to Vehicle.
    class Vehicle {
      String toString() {
        return "The radar reports: VIN " +  VIN + 
               " owned by " + owner + 
               " heading " + currentDirection + 
               " (degrees) " +
               " at a speed of " + speed); 
      }
      private int currentSpeed;
      int getSpeed() { return currentSpeed; }
      void changeSpeed(int newSpeed) { currentSpeed = newSpeed; }
      void stop() { currentSpeed = 0; }
      private int currentDirection; 
      int getCurrentDirection() { return currentDirection; }
      void setDirection(int newDirection) { currentDirection = newDirection % 360; }
      static final String TURN_LEFT = "Turn left"; 
      static final String TURN_LEFT = "Turn left"; 
      void turn(int degrees) { currentDirection = (currentDirection + degrees) % 360; } 
      void turn(String direction) {
        if (direction.equals(left)) { currentDirection = (currentDirection + 90) % 360; }
        else { currentDirection = (currentDirection - 90) % 360; } 
      }
      private String owner; 
      int getOwner() { return owner; }
      void setOwner(String newOwner) { owner = newOwner; }
      private static int highestVIN; 
      static int highestVIN() { return highestVIN; } 
      private int VIN;
      Vehicle() { VIN = highestVIN++; }
      Vehicle (String owner) { this(); this.owner = owner; }
      public static void main(String[] args) {
        // ... 
      }
    }


10. Define a class of Fractions

Consider ordinary fractions like 3/4 and -1/2. A fraction is the ratio of two integers: a numerator and a denominator. Create a user-defined type for fractions by defining a class Fraction. The class should supply a set of necessary operations on fractions

and should hide implementation details of data representation and internal manipulations.

Remember that a class encapsulates data structures with access and manipulation procedures. In designing a class, an important task is to decide on the internal data representation which is isolated from the outside view. Member methods will keep the data representation consistent. Outside methods are not allowed to create or modify the internal structures directly. They may do this by calling methods in the class.

Here are some (internal) representation considerations to guide your design:

Your Fraction class can be designed to enforce such conventions, not just to have them as principles that one can choose to follow or ignore. This is one big advantage of object-oriented programming. The canonical representation conventions should be enforced by the Fraction constructor.

The constructor should take the two arguments n and d and construct a fraction n/d out of it. The denominator of the resulting Fraction should not be zero or negative. The fraction should be reduced by removing the gcd (greatest common divisor) between the numerator and the denominator. You should define a member method gcd that computes the gratest common divisor of two numbers and make the constructor use it to create canonical Fractions. gcd should be implemented as a classwide method since it is independent of Fraction instances. It should be declared private because it does not contribute to the public interface of the Fraction class.

gcd is described in your text on page 149.

The operations for fractions listed above should implement binary operations. In other words we are going to define methods such that a Fraction can be added to, subtracted from, multiplied with, or divided by another Fraction. The result should be a Fraction.

You should also define a few predicates.

Each Fraction that you create should be able to respond to the following questions:

You should also add a toString method to Fraction (an instance method) that returns a String representation of a Fraction. It must be an instance method as it is

Testing your Fractions:

// TestFrac.java

class TestFrac {
  public static void main(String[] args) {
    Fraction x = new Fraction( 1, 20); 
    Fraction u = new Fraction(-1, 60); 
    Fraction v = new Fraction( 1, 30); 
    Fraction y; 
    y = x.plus(u).minus(v); // in one step!
    System.out.println( x + " + " + 
                        u + " - " + 
                        v + " = " + y); 
  }
}

For the ambitious:

Add a private method lcm to the class Fraction that takes two ints and returns their least common multiple. (For example, lcm(15, 12) is 60. Hint: use gcd). Modify minus and plus in Fraction to use lcm.

// File: Fraction.java

public class Fraction {
  private int num;    // numerator 
  private int denom;  // denominator 
  public Fraction(int n, int d) {
    int g; 
    if (d == 0) { 
      System.out.println("Fraction: fraction "
      + "with 0 denominator?"); System.exit(1); 
    } 
    if (n == 0) {
      num = 0; 
      denom = 1; 
    } else {
      if (d < 0) {
        n = -n; 
        d = -d; 
      }
      if ((g = gcd(n, d)) != 1) { // remove gcd
        n /= g;
        d /= g;
      }
      num = n; 
      denom = d; 
    }
  } 
  private static int gcd(int a, int b) {
    a = Math.abs(a); 
    b = Math.abs(b); 
    if (a == 0) return b; // 0 is error value 
    if (b == 0) return a; 
    int t; 
    while (b > 0) {
      t = a % b; // take "-" to the extreme 
      a = b; 
      b = t; 
    }
    return a; 
  }
  public boolean isZero() {
    return (denom == 1 && num == 0); 
  } 
  public boolean isInt() {
    return (denom == 1); 
  }
  public boolean equals(Fraction y) {
    return (num == y.num && denom == y.denom); 
  }
  public boolean greaterThan(Fraction y) {
    return (num * y.denom > denom * y.num); 
  }
  public Fraction minus(Fraction y) {
    return new Fraction(
      num * y.denom - y.num * denom, 
      denom * y.denom
    ); 
  }  
  public Fraction plus(Fraction y) {
    return new Fraction(
      num * y.denom + y.num * denom, 
      denom * y.denom
    ); 
  }  
  public Fraction times(Fraction y) {
    return new Fraction(num * y.num, denom * y.denom); 
  }  
  public Fraction dividedBy(Fraction y) {
    return new Fraction(num * y.denom, denom * y.num); 
  }  
}
The part for the ambitious will be posted here.

If you have any questions please let us know.