import draw.*; /* --- CSU213 Spring 2005 Lecture Notes --------- Copyright 2005 Viera K. Proulx Lecture 9: All people are equal, but some are more equal than others... Part 1 */ /* Goals: - learn to determine the equality of two self-referential data - learn to sort lists In the last problem in the previous lecture we produced a list of CDs. However, we could not design proper tests for this method, because we did not know how to determine whether two lists are the same.We have to define our own comparison methods 'same'. We will continue with the class CD and the classes that represent a list of CDs. 1. Problem analysis. We need the method 'same' to compare one instance of the list of CDs with another instance. The argument must be of the type ALoCD, because it can be an empty list as well as a nonempty list. 2. Purpose and Header. // determine whether this list of CDs is the same as the given one abstract boolean same(ALoCD that); This method needs to be defined in every subclass of the ALoCD class. 3. Examples. Empty list can appear both as the instance that invokes the method ('this') and as the argument to the method ('that'). Additionally, we need to think what happens when two lists contain the same objects, but not in the same order. It is much easier to compare two lists if the order of the items is the same in both of them. We defer till later the work on sorting the lists, and require here that the comparison succeeds only if the objects appear in the same order in both lists. Our examples then need to address all of these possibilities: // dealing with the empty list mtcd.same(new MTLoCD) --> true mtcd.same(list1) --> false list1.same(mtcd) --> false list4.same(mtcd) --> false // dealing with a list with one item list1.same(new ConsLoCD(help, mtcd)) --> true list1.same(list2)) --> false list1.same(new ConsLoCD(help, new ConsLoCD(pearl, mtcd))) --> false // dealing with a list with more than one item list4.same(new ConsLoCD(help, mtcd)) --> false list4.same(new ConsLoCD(tool, new ConsLoCD(boston, list2)) --> true list4.same(new ConsLoCD(tool, new ConsLoCD(boston, mtcd)) --> false 4. Template. Again, there is no data in MTLoCD, so we only need a template for the class ConsLoCD: ... this.first ... ... this.first.same(CD ...) ... ... this.rest ... ... this.rest.same(ALoCD ...) ... 5. Body. As usual we try to do the simple case first. class MTLoCD: ------------- In the class MTLoCD we have two pieces of data: 'this' and 'that'. We do not know whether 'that' is an instance of MTLoCD or of the ConsLoCD. However, we do know that 'this' is an instance of MTLoCD. We decide to use this fact and design a helper method that consumes an argument of the type MTLoCD and determines whether some list is the same as the given empty list: // determine whether this list is the same as given empty list boolean sameMTLoCD(MTLoCD other) and the body of our method becomes: return that.sameMTLoCD(this); What we see here is the reversal of the role of the two pieces of data involved in the method: the argument of the unknown type invokes the helper method, using the data of a known type ('this') as the argument. But that means, the method 'sameMTLoCD' has to be implemented for all lists - whether an empty or the constructed one. Let us finish the design of the bodies (a trivila taks) - leaving the examples to you: in the class ALoCD: // determine whether this list is the same as given MTLoCD list abstract boolean sameMTLoCD(MTLoCD other); in the class MTLoCD: // determine whether this list is the same as given MTLoCD list boolean sameMTLoCD(MTLoCD other){ return true; } in the class ConsLoCD: // determine whether this list is the same as given MTLoCD list abstract boolean sameMTLoCD(MTLoCD other){ return false; } class ConsLoCD: ------------- Here we run into the same problem. If the second list was an instance of ConsLoCD, we could compare the 'first' in each and the 'rest in each. But 'that' can also be an instance of MTLoCD. We again wish for a helper method that gets as argument an instance of ConsLoCD: // determine whether this list is the same as given ConsLoCD list boolean sameConsLoCD(ConsLoCD other) and the body of the original method becomes: return that.sameConsLoCD(this); This method is invoked by the list that can be either an instance of MTLoCD or an instance of ConsLoCD, but we know its argument is an instance of ConsLoCD. Again, we must define this method for both the MTLoCD and the ConsLoCD class. Here is the variant in the MTLoCD class: // determine whether this list is the same as given ConsLoCD list boolean sameConsLoCD(ConsLoCD other){ return false; } All that remains is the body of the method in the ConsLoCD class. But here both 'this' and 'other' are of the type ConsLoCD. The template gives us the following: ... this.first ... ... other.first ... ... this.rest ... ... other.rest ... and also ... this.first.same(CD ---) ... ... this.rest.same(ALoCD ---) ... ... other.first.same(CD ---) ... ... other.rest.same(ALoCD ---) ... We see that we can compare the two 'first' fields (both of the type CD): 'this.first.same(other.first)' which invokes the method 'same' in the class CD. Reading the purpose statement for 'this.rest.same' method invocation we get: 'determine whether the rest of this list is the same as given list' and all we have to do is to use the 'other.rest' as its argument, to compare the 'rests' of the two lists: 'this.rest.same(other.rest)' which invokes the method 'same' in the class that is the runtime type of 'this.rest'. The body of the method is then: // determine whether this list is the same as given ConsLoCD list boolean sameConsLoCD(ConsLoCD other){ return this.first.same(other.first) && this.rest.same(other.rest); } */ // The final code is: /* ; ; ; ; ;;;; ; ; ; ; ; ; ; ; ; ; ;;; ;; ; ;;; ; ; ; ; ; ;; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ;;;;;; ; ; ; ; ; ; ; ; ; ; ; ; ; ;; ; ; ;;;; ;;; ;; ; ;;;; ; ; ; */ /* +-----------------------+ | CD | +-----------------------+ | String title | | int tracks | +-----------------------+ | boolean same(CD that) | +-----------------------+ */ // to represent a CD class CD { String title; int tracks; CD(String title, int tracks) { this.title = title; this.tracks = tracks; } boolean same(CD that){ return this.title.equals(that.title) && this.tracks == that.tracks; } } /* +-------+ | ALoCD | +-------+ +-------+ / \ --- | ----------------- | | +--------+ +------------+ | MTLoCD | | ConsLoCD | +--------+ +------------+ +--------+ | CD first | | ALoCd rest | +------------+ */ // to represent a list of CDs abstract class ALoCD { // determine whether this list of CDs is the same as the given one abstract boolean same(ALoCD that); // determine whether this list is the same as given MTLoCD list abstract boolean sameMTLoCD(MTLoCD other); // determine whether this list is the same as given ConsLoCD list abstract boolean sameConsLoCD(ConsLoCD other); // produce a list of all long CDs (> t10 tracks) in this list abstract ALoCD longCDs(); } // to represent an empty llist of CDs class MTLoCD extends ALoCD { MTLoCD() {} // determine whether this list of CDs is the same as the given one boolean same(ALoCD that){ return that.sameMTLoCD(this); } // determine whether this list is the same as given MTLoCD list boolean sameMTLoCD(MTLoCD other){ return true; } // determine whether this list is the same as given ConsLoCD list boolean sameConsLoCD(ConsLoCD other){ return false; } ALoCD longCDs(){ return this; } } // to represnet a non-empty list of CDs class ConsLoCD extends ALoCD { CD first; ALoCD rest; ConsLoCD(CD first, ALoCD rest) { this.first = first; this.rest = rest; } // determine whether this list of CDs is the same as the given one boolean same(ALoCD that){ return that.sameConsLoCD(this); } // determine whether this list is the same as given MTLoCD list boolean sameMTLoCD(MTLoCD other){ return false; } // determine whether this list is the same as given ConsLoCD list boolean sameConsLoCD(ConsLoCD other){ return this.first.same(other.first) && this.rest.same(other.rest); } ALoCD longCDs(){ if (this.first.tracks > 10) return new ConsLoCD(this.first, this.rest.longCDs()); else return this.rest.longCDs();} } class Examples { Examples(){} CD help = new CD("Help", 12); CD pearl = new CD("Pearl", 8); CD tool = new CD("Tool", 13); CD boston = new CD("Boston", 10); ALoCD mtcd = new MTLoCD(); ALoCD list1 = new ConsLoCD(this.help, this.mtcd); ALoCD list2 = new ConsLoCD(this.pearl,this.list1); ALoCD list4 = new ConsLoCD(this.tool, new ConsLoCD(this.boston, this.list2)); // test the method totalTracks boolean testLongCD(){ return this.mtcd.longCDs().same(this.mtcd) && this.list1.longCDs().same(this.list1) && this.list2.longCDs().same(this.list1) && this.list4.longCDs().same(new ConsLoCD(this.tool, new ConsLoCD(this.help, this.mtcd))); } boolean testAll(){ return this.testLongCD(); } }