// Lab 6: Object polymorphism, Interfaces, and Function Objects /* In this lab you will learn to build a list of Objects - abstracting from the lists of various kinds of data we have seen in the past. You will also learn to abstract over the behavior of classes through interfaces, and to construct 'function objects' - as instances of classes that represent only functional behavior. The goal of our program today is to keep a record of a cross country trip in a car. At each point when we refuel, we record the starting point of this segment, the destination where we refueled, the miles traveled, the amount of gasoline purchased (in gallons) and the price of the fuel per gallon, given in whole cents. We also want to keep track of the souvenirs we buy on the trip. We record the name of the souvenir, how many souvenirs we bought, and the price per item. The following class represents one segment of the trip: /* +--------------------+ | Segment | +--------------------+ | String origin | | String destination | | int miles | | int gallons | | int price | +--------------------+ ; ; ; ; ;;;; ; ; ; ; ; ; ; ; ;;; ;; ; ; ;; ;; ;;; ; ;; ;;;; ; ;; ; ; ; ;; ;; ;; ; ; ; ;; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ;;;;;; ; ; ; ; ; ;;;;;; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ;; ; ; ; ; ; ; ; ; ;;; ;;;; ;; ; ; ; ; ;;;; ; ; ;; ; ; ; ; ; ; ;;;; */ // to represent one segment of a road trip class Segment { String origin; String destination; int miles; int gallons; int price; Segment(String origin, String destination, int miles, int gallons, int price) { this.origin = origin; this.destination = destination; this.miles = miles; this.gallons = gallons; this.price = price; } /* // ** DRAFT TEMPLATE ** Edit as needed. ??? mmm() { ... this.origin ... ... this.destination ... ... this.miles ... ... this.gallons ... ... this.price ... } */ } //////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////// /* Make examples of trip segments in the class Examples. Next, design the classes to represent a list of Objects, and make examples of some parts of the cross country trip, as data in these classes. */ //////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////// /* ; ; ; ; ; ; ;;;; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ;;; ; ; ; ;; ; ; ; ; ; ; ; ; ; ;; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ;;;;;;; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ;; ; ; ; ; ; ;;;;;; ;;; ;;;; ; ;; ; ; ; ; ; ; ;; */ //////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////// /* To make it possible to compare the results, we need to include the method 'same' in all of these classes. We make the function header 'universal' so that one header works for each of these classes and define an interface that represents this behavior: */ /* ; ; ; ; ; ;;;; ; ; ; ; ; ; ; ; ; ; ;;; ; ;; ;; ;;; ; ; ;; ; ; ;; ;; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ;;;; ; ; ; ;;;;;; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ;;; ;;;;; ; ; ; ;;;; ; ; ; */ interface ISame{ // determine whether this object is the same as the given one boolean same(Object obj); } /* Add the method 'same' to the class Segment and indicate that it implements the ISame interface. To extract the fields from the given Object obj, you will need to cast obj to the type Segment. For example, you may write: this.price == ((Segment)obj).price as a part of your condition. Make sure you follow the design recipe. You may assume that the given object will be of the type Segment. //////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////// Now design the class to represent the souvenir purchases, and make sure it too implements the ISame interface. Record the price per item in whole cents as well as the number of items you have purchased. Make examples of lists of souvenirs. //////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////// Now design the method 'same' for the classes that represent a list of objects. We already know how to do this, if the method argument is of the type ALoObj. But we can make this happen as follows. In the class ALoObj we definethe method: // determine whether this list of objects is the same // as the given object boolean same(Object obj){ return sameALoObj((ALoObj)obj); } and add to our wish list the method // determine whether this list of objects is the same // as the given list of objects boolean sameALoObj(ALoObj obj) We can now design this method in the classes the represent a list of objects. Do it now. //////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////// Next design the method totalMiles that computes the total number of miles traveled on a trip. Then design the method totalCost that compute how much money we spent on souvenirs during this trip. //////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////// Observe the similarites between these two sets of methods. Define an interface IValue to represent a method that produces an integer that represents the value of this object. //////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////// Now modify both classes to implement this interface, combine the two methods into one method totalValue using the fact that both implement the IValue interface. Test your original examples, to make sure they still work. //////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////// Now compute the total number of gallons of gas consumed during the trip. Notice, that even though we are repeating the same computation, we cannot re-use the IValue interface. Instead, we define a new interface: */ /* ; ; ; ; ; ;;;; ; ; ;;;; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ;; ; ; ; ; ;; ;;;; ; ; ; ; ;; ; ; ; ; ;; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ;; ; ; ; ; ; ; ; ; ; ;;;; ; ;; ; ;;;;;; ; ; ; ;; ; ; ; ; ; ;; */ interface IObj2Int{ // produce an integer that represents the value of the given object int objValue(Object obj); } /* Any class that implements this interface will have the method that computes the value of a given object. This can be any object, not just an instance of the class wehre this method is defined. Indeed, we can define: */ /* ; ; ; ; ;;;; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ;;; ;;; ;;;; ; ; ;;; ; ; ; ;;; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ;; ; ; ; ; ; ; ; ; ; ; ; ; ; ;; ; ; ; ;;;; ; ; ; ;;;;;; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ;; ; ; ;;;; ;;; ;;; ;; ; ;;;;; ; ;; ; ;;;; ; ; ; */ // class to represent a method that produces the cost of a trip segment class CostValue implements IObj2Int{ int objValue(Object obj){ return ((Segment)obj).gallons * ((Segment)obj).price; } } /* The class costValue has no fields and contains just one method needed to implement the desired interface. So, we can compute the cost of the following trip segment: Segment b2nyc = new Segment("Boston", "NYC", 200, 10, 210); as follows: CostValue cv = new CostValue(); int cost = cv.objValue(b2nyc); boolean testCost cost == 2100; Try this in the Examples class. Make additional examples of trip segments and the cost computation that uses the instance of the CostValue class. Do the same for the MilesValue class defined below:: */ /* ; ; ; ; ; ; ; ; ; ; ; ; ;; ;; ; ; ; ; ; ;; ;; ; ; ; ; ; ; ; ; ; ; ; ;;; ;;; ; ; ;;; ; ; ; ;;; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ;; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ;;;;;; ;; ; ; ;;;; ; ; ; ;;;;;; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ;; ; ; ; ; ; ; ;;;; ;;; ; ;;;;; ; ;; ; ;;;; ; ; ; */ class MilesValue implements IObj2Int{ int objValue(Object obj){ return ((Segment)obj).miles; } } /* Finally, abstract over the two methods in the classes that represent the list of objects, so that the new method 'totalValue2' consumes an argument which is an instance of a class that implements the IObj2Int interface and uses it's objValue method to compute the value of the .first item. ------ the code you need ------------------------------------------ in the class ALoObj: // compute the total value of objects in this list abstract int totalValue2(IObj2Int value); in the class MTLoObj: int totalValue2(IObj2Int value){ return 0; } in the class ConsLoObj: int totalValue2(IObj2Int value){ return value.objValue(this.first) + this.rest.totalValue2(value); } ------------------------------------------------------ Next, make examples of computing the total cost of trip, using your earlier sample trips, and using the CostValue instance defined above. Now copy your class that represents souvenirs from Part one, and compute the total cost of souvenirs. You will first have to define a class that contains the objValue method that computes the cost of one souvenir purchase. Can you use this technique to count the number of different souvenir purchases, or the total number of souvenir items? */ /* */ /* ; ; ; ; ;;;;;; ; ; ; ; ; ; ; ; ; ; ; ;;; ; ;; ;; ; ;; ; ;;; ;;; ; ;;;;; ; ; ; ; ;; ;; ; ;; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ;; ; ; ; ;;;; ; ; ; ; ; ; ;;;;;; ;; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ;; ; ; ; ; ; ;;;;;;; ; ;;;;; ; ; ; ; ;; ; ;;;; ;;; ; ; ; ; ; ; */ class Examples{ Examples() {} }