Purpose:
To practice designing and using function-classes/objects, to become accustomed to parametrized types (abstracting over datatypes), and to start using mutation.
Part 1: Simple Parameterized Classes
Today in lecture we introduced our final abstraction: abstraction over datatypes. In Racket that was as simple as a comment with unknown kinds of things (like X and Y), since structures can hold any kinds of values:
;; A [Pair-of X Y] is: (make-pair X Y) (define-struct pair (left right))In Java everything is required to have a type at the time of writing our program. This means that comments describing our data definitions are not good enough.Exercise 1:
- Start by designing a class that represents a pair of strings with left and right components. Call the class PairStringString (for reasons that will be clear soon).
- Design the methods, getLeft and getRight that return the left and right parts of the structure, respectively.
- Make some examples and a few trivial tests of your methods.
Exercise 2:
Now, we abstract!!
- Now design a class that represents a pair of int and double components. Call the class PairIntDouble.
- Again, design the methods getLeft and getRight that return the left and right parts of the structure, respectively.
- Make some more examples and again a few trivial tests of your methods.
Exercise 3:
- Abstract your two Pair... classes into a single, parametrized class. Your class should have two type parameters (separated by commas) surrounded by special brackets: <...>. Also update the methods (getLeft and getRight).
Note: there's no need for the parameters on the constructor, just on the constructor's parameters.
- Take your examples from before and repeat them with your new (abstracted) definition.
Note: you can't use int and double as type parameters... instead you have to use the Object equivalents: Integer and Double
Part 2: Finally, lists of anything...
Here are two similar class/interface definitions:
// Lists of Strings abstract class ListString{} class EmptyString extends ListString{ EmptyString(){} } class ConsString extends ListString{ String first; ListString rest; ConsString(String first, ListString rest){ this.first = first; this.rest = rest; } }// Lists of integers abstract class ListInt{} class EmptyInt extends ListInt{ EmptyInt(){} } class ConsInt extends ListInt{ int first; ListInt rest; ConsInt(int first, ListInt rest){ this.first = first; this.rest = rest; } }Exercise 4:
- Abstract these two definitions into a single class-hierarchy to represent lists with arbitrary element types. Don't forget purpose statements!!!
- Make examples of different lists that contain Strings, and Integers (but not at the same time).
Exercise 5:
Design the method length for your classes that represent lists. Clearly this determines the number of elements in this list.Exercise 6:
Design the method asStrings that returns a list of all the elements in this list as Strings.What type of list does the methods return? Empty is a bit of a special case... why can't you just return this anymore?
Hint: you can use ""+something to convert something into a String
Part 3: Mutable Structures
In this part we design a very traditional mutable structure: bank accounts.Exercise 1:
Design a class hierarchy to represent bank Accounts. An account is one of Checking (for writing checks or using a check-card), Savings (higher interest rate, but less accessible), or Credit (where the customer borrows money from the bank at a given interest rate).
Each account has an id number and a name, interest rate, and current balance. In addition, a checking account has a minimum balance (customers must keep that much in the account), and a credit account has a maximum balance (the maximum amount that the customer can borrow).
Note that Credit account balance is the opposite of checking and savings; it's the amount the customer owes, not the amount deposited.Exercise 2:
Design a method getBalance for your account classes that returns the account's current balance. In which class should you implement it? (Test your methods!!)
Exercise 3:
Design a method maxWithdraw for your account classes that returns the maximum amount that the customer can withdraw. Where do you have to implement it? Note, that at least one class must be different. Don't forget about the special cases of Checking and Credit accounts.
Note that the testing above is pretty easy right? Well, below you'll need to create a reset method that initializes your examples to a known state. Then you can run your new method and confirm that the instance has changed.
For testing void methods, your test method will also be void. You can just list your statements (executed for effect) in the body of the method, without the need for a return.
Exercise 4:
Design the void method withdraw for your account classes that withdraws the given amount by changing the state of this account. Don't forget your Purpose and Effect statements.
Note: if the given amount is greater than the max withdrawal, then you should throw an exception.Exercise 5:
Design the void method deposit for your account classes that deposits the given amount by changing the state of this account. Don't forget your Purpose and Effect statements.
Note: a Credit account cannot have a balance of less than 0.