cs205: engineering software? |
(none) 25 June 2008 |
Problem Set 4 Subtyping and Inheritance |
Out: 20 September Due: Friday, 6 October (beginning of class) |
Collaboration Policy. For this problem set, you may either work alone and turn in a problem set with just your name on it, or work with one other student in your section of your choice. If you work with a partner, you and your partner should turn in one assignment with both of your names on it.
Regardless of whether you work alone or with a partner, you are encouraged to discuss this assignment with other students in the class and ask and provide help in useful ways. You may consult any outside resources you wish including books, papers, web sites and people. If you use resources other than the class materials, indicate what you used along with your answer.
Liskov's Chapter 7 and Meyer's Static Typing and Other Mysteries of Life describe very different rules for subtypes.
Liskov's substitution principle requires that the subtype specification supports reasoning based on the supertype specification. When we reasoned about a call to a supertype method, we reasoned that if a callsite satisfies the preconditions in the requires clause, it can assume the state after the call satisfies the postconditions in the effects clause. This means the subtype replacement for the method cannot make the precondition stronger since then our reasoning about the callsite may no longer hold (that is, the callsite may not satisfy the stronger precondition). Hence, the type of the return value of the subtype method must be a subtype of the type of the return value for the supertype method; the types of the parameters of the subtype method must be supertypes of the types of the parameters of the supertype method. This is known as contravariant typing.
Bertrand Meyer prefers covariant typing: the subtype replacement method parameter types must be subtypes of the types of the parameters of the supertype method. We will generalize his rules to apply to preconditions and postconditions also: the subtype method preconditions must be stronger than the supertype method precondition (presub => presuper) and the subtype postconditions must be stronger than the supertype postconditions (postsub => postsuper). Note that unlike the corresponding Liskov substitution rule, (presuper && postsub) => postsuper, there is no need for presuper in the covariant rule since postsub => postsuper.
The signature rule in Java is stricter: subtype methods must have the exact same return and parameter types of the method they override, although they may throw fewer exception types. Java does not place constraints on the behavior of methods, however, since the compiler is not able to check this.
Consider the minimal Tree class and its BinaryTree subtype, both specified on the next page. The Java compiler will not allow the getChild method of BinaryTree. Here is the error message:
BinaryTree.java:29: getChild(int) in BinaryTree cannot override getChild(int) in Tree; attempting to use incompatible return type found : BinaryTree required: Tree
public class Tree // OVERVIEW: A Tree is a mutable tree where the nodes are int values. // A typical Tree is < value, [ children ] > // where value is the int value of the root of the tree // and children is a sequence of zero or more Tree objects // that are the children of this tree node. // A Tree may not contain cycles, and may not contain the same // Tree object as a sub-tree in more than one place. public Tree (int val) // EFFECTS: Creates a tree with value val and no children: // < value, [] > public void addChild (Tree t) // REQUIRES: t is not contained in this. // MODIFIES: this // EFFECTS: Adds t to the children of this, as the rightmost child: // this_post = < this_pre.value, children > // where children = [ this_pre.children[0], this_pre.children[1], ..., // this_pre.children[this_pre.children.length - 1], t] // NOTE: the rep is exposed! public Tree getChild (int n) // REQUIRES: 0 <= n < children.length // EFFECTS: Returns the Tree that is the nth leftmost child // of this. // NOTE: the rep is exposed!
public class BinaryTree extends Tree // OVERVIEW: A BinaryTree is a mutable tree where the nodes are int values // and each node has zero, one or two children. // // A typical BinaryTree is < value, [ children ] > // where value is the int value of the root of the tree // and children is a sequence of zero, one or two BinaryTree objects // that are the children of this tree node. // A BinaryTree may not contain cycles, and may not contain the same // BinaryTree object as a sub-tree in more than one place. public BinaryTree (int val) // EFFECTS: Creates a tree with value val and no children: // < value, null, null > public void addChild (BinaryTree t) // REQUIRES: t is not contained in this and this has zero or one children. // MODIFIES: this // EFFECTS (same as supertype): // Adds t to the children of this, as the rightmost child: // this_post = < this_pre.value, children > // where children = [this_pre.children[0], this_pre.children[1], ..., // this_pre.children[this_pre.children.length - 1], t] public BinaryTree getChild (int n) // REQUIRES: 0 <= n < 2 // EFFECTS: If this has at least n children, returns a copy of the BinaryTree // that is the nth leftmost child of this. Otherwise, returns null.
2. (10) Does the addChild method in BinaryTree satisfy the Liskov substitution principle? Explain why or why not.
3. (10) Does the getChild method in BinaryTree satisfy the Eiffel subtyping rules? Explain why or why not.
4. (10) Does the addChild method in BinaryTree satisfy the Eiffel subtyping rules? Explain why or why not.
Note that the Java compiler will allow the addChild method, but it overloads instead of overrides the supertype addChild method. That is, according to the Java rules the BinaryTree class now has two addChild methods — one is the addChild (Tree) method inherited from Tree, and the other is the addChild (BinaryTree) method implemented by BinaryTree. This can be quite dangerous since the overloaded methods are resolve based on apparent types, not actual types. For example, try this program:
static public void main (String args[]) { Tree t = new BinaryTree (3); BinaryTree bt = new BinaryTree (4); t.addChild (bt); // Calls the addChild(Tree) method bt.addChild (new BinaryTree (5)); // Calls the addChild (BinaryTree) method bt.addChild (new Tree (12)); // Calls the addChild (Tree) method }Note that the first call uses the inherited addChild(Tree) because the apparent type of t is Tree, even though its actual type is BinaryTree.
-Xmx512m -eaThe first argument sets the maximum size of the VM heap to 512 megabytes. The second argument turns on assertion checking.
Rhocasa provides a graphical user interface (GUI) for manipulating a set of images by applying filters to generate new images. Every filter must be a subtype of the Filter datatype. The filters provided are shown in the class hierarchy below:
The Filter class is a subtype of java.lang.Object, the ultimate supertype of all Java object types. The Filter class provides methods for examining and manipulating an image including the filter abstract method. Since filter is an abstract method, the Filter abstract class provides no implementation of filter. To make a useful Filter object, we need a subclass of Filter that provides an implementation of the filter method.java.lang.Object ps4.Filter ps4.PointFilter ps4.BrightenFilter ps4.GreyscaleFilter ps4.BlurFilter ps4.FlipFilter ps4.MultiFilter ps4.AddFilter ps4.TileFilter
We provide two abstract subtypes of Filter:
For the next two questions, you are to implement new filters. The behavior of these filters is not specified precisely; you can determine in your implemention a good way to provide the effect described.
7. (10) Develop a new filter that produces an image that is the "average" of two or more images.
10. (10) Modify the application to suppoed the parameterized filters. In addition to modifying the filter classes, you will need to modify the GUI to allow a user to enter the parameter for a parameterized filter. This will involve modifying the GUIHandler.actionPerformed method defined in GUI.java. Hint: look at how the MultiFilter is handled.
Turn-in Checklist: You should turn in your answers to questions 1-10 on paper at the beginning of class on Friday, 6 October. including your code (but not unnecessary printouts of the provided code). Also, submit the image you produced for question 11 as a JPG and a zip file containing all of your code by email to evans@cs.virginia.edu. There may be a token prize for the best image created.