770 likes | 948 Views
Type Hierarchy . Gang Qian Department of Computer Science University of Central Oklahoma. Objectives. Introduction to type families Specifying a type hierarchy Type hierarchy in Java Abstract classes Multiple implementations Substitution principle Discussion.
E N D
Type Hierarchy Gang Qian Department of Computer Science University of Central Oklahoma
Objectives • Introduction to type families • Specifying a type hierarchy • Type hierarchy in Java • Abstract classes • Multiple implementations • Substitution principle • Discussion
Introduction to Type Families • We can enhance data abstractions by defining families of related data types • Data types of the same type family have similar behaviors: • All have certain methods • Those methods behave similarly • They differ by: • extending the behavior of the common methods, and • providing additional methods • Examples: • Real world: Mammals, Dogs and Cats • Programs: Reader and BufferedReader
A type family is defined by a type hierarchy • A type at the top of the hierarchy defines the behavior common to all family members • Called supertype • Other family members are subtypes • The hierarchy may have more than two levels • A type may be supertype and subtype at the same time • A good way to check superclass-subclass relationship is to use “is-A” • If we can say “Every instance of type X is also an instance of type Y”, then X is a subtype of Y
A type family can be used in two different ways: • Define multiple implementations of a type • Subtypes do not add any new behavior, except that each has its own constructors • The subtypes implement exactly the behavior given the specification of the supertype • E.g., we can have a type family that provides both sparse and dense polynomials • Subtypes extend the behavior of their supertype • Such a type family can be multilevel • There might be multiple implementations involved too
Type hierarchy requires the members of the type family to have related behaviors: Substitution Principle: • The behavior of the supertype must be supported by subtypes • Subtype objects can be substituted for supertype objects without affecting the behavior of the using code • The substitution principle allows the using code to be written based on supertype specification, yet work correctly when using objects of the subtype • The principle provides abstraction by specification for a type family • Further discussed later in the lecture
Review: Assignment and Dispatching in Java • Assignment • A variable declared to be one type can actually refer to an object of some subtype • Can happen in assignments and arguments passing • Apparent type vs. actual type • The actual type is always a subtype of the apparent type • Note that a type is a subtype of itself • The compiler does type checking based on apparent types
Dispatching (Dynamic Binding) • The compiler may not be able to determine what code to run when a method is called • It is determined by the actual type of the object • Calling the right method is achieved by a mechanism called dispatching • See page 151 of the text book
Specifying a Type Hierarchy • The first step is to specify the top type • Very similar to those of the other data abstractions • The specification may be incomplete • E.g., it may lack constructors • The specification of the subtypes are given relative to those of their supertypes • Focus on what is new from the super type: • Constructors and additional methods • If the subtype changes the behavior of some supertype methods, specification must be specified for those methods • Only limited changes are allowed (the substitution principle)
Implementations of supertypes can be different from those of regular data abstractions • Some supertypes are not implemented at all • Others may have only partial implementations • A supertype may provide extra information to subtypes • Protected instance variables and/or methods
Defining Hierarchy in Java • Supertypes in Java can be defined by either classes or interfaces • An interface only defines a specification • It has no implementations • A class as a supertype may provide partial or full implementations • Java has two kinds of classes: • Concrete classes provide a complete implementation • Abstract classes provide at most partial implementations • They have no objects
Classes may have normal methods and final methods • Final methods cannot be overridden / re-implemented by subclasses • Final methods are useful in fixing the behavior of a method inherited from the supertype • Abstract classes may have abstract methods that are not implemented • They must be implemented by some subclass • It is only of interest to implementers, not users
A concrete subclass: • must implement its constructors and any extra method • may override some methods of its superclass • Any method that are overridden must have the same signature as that of the superclass, except that: • the subclass method may have a different return type, and • can throw fewer exception types • inherits from the superclass the final methods and other methods that are not overridden
The rep of a subclass contains the rep of the superclass • If the subclass need to interact with the superclass, it is best if it can do it via the public interface of the superclass • Preserves full data abstraction: • Superclass can be re-implemented without affecting the subclasses • Problem: May be inefficient • Superclass may also provide protected constructors, methods and instance variables • Note that they are also package visible
Example: IntSet • Specification of IntSet as a superclass /** OVERVIEW: IntSets are mutable, unbounded sets of integers. A typical IntSet is {x1,...,xn}. */ public class IntSet { /** EFFECTS: Constructor. Initializes this to be empty. */ public IntSet () /** MODIFIES: this EFFECTS: Adds x to the elements of this, i.e., this_post = this + { x }. */ public void insert (int x) (continued on next slide)
/** MODIFIES: this EFFECTS: Removes x from this, i.e., this_post = this — { x }. */ public void remove (int x) /** EFFECTS: If x is in this returns true else returns false. */ public boolean isIn (int x) /** EFFECTS: Returns the cardinality of this. */ public int size () /** EFFECTS: Returns a generator that provides all elements of this (as Integers), each exactly once, in arbitrary order. REQUIRES: this not to modified while the generator is in use. */ public Iterator<Integer> elements () (continued on next slide)
/** EFFECTS: Returns true if this is a subset of s; else returns false. */ public boolean subset (IntSet s) public boolean repOK(); } • Note that there is a new subset method
Implementation of IntSet (Specification not included) public class IntSet { private Vector<Integer> els; // the elements // other implementations ... public boolean subset (IntSet s) { if (s == null) return false; for (int i = 0; i < els.size(); i++) if (!s.isIn(els.get(i))) return false; return true; } }
Specification of a subtype MaxIntSet /** OVERVIEW: MaxIntSet is a subtype of IntSet with an additional method, max, to determine the maximum element of the set. */ public class MaxIntSet extends IntSet { /** EFFECTS: Makes this be the empty MaxIntSet. */ public MaxIntSet () /** EFFECTS: If this is empty throws EmptyException; else returns the largest element of this. */ public int max () throws EmptyException }
Note that the specification of the subclass relies on the specification of the superclass, and only defines what is new • Implementations of MaxIntSet • An easy way to implement MaxIntSet is to keep track of the maximum element in an extra instance variable biggest // the biggest element if set is not empty private int biggest; • Abstraction Function?: AF(c) • Rep Invariant?: I(c)
The abstraction function and the rep invariant of a subclass are typically defined based on those for the superclass • Abstraction functions: // The abstraction function is: // AF_MaxIntSet(c) = AF_IntSet(c) • Note the notation to distinguish the two abstraction function • Or in plain English: // The abstraction function is: // the same as the AF of IntSet
Rep Invariant: // The rep invariant is: // I_MaxIntSet(c) = c.size > 0 => // ( c.biggest in AF_IntSet(c) && // for any x in AF_IntSet(c) (x <= c.biggest) ) • Or in plain English: // The rep invariant is: // If the MaxIntSet is not empty, biggest // contains the greatest value in MaxIntSet
public class MaxIntSet extends IntSet { // the biggest element if set is not empty private int biggest; public MaxIntSet () { super(); } public void insert (int x) { if (size() == 0 || x > biggest) biggest = x; super.insert(x); } public void remove (int x) { super.remove(x); if (size() == 0 || x < biggest) return; Iterator<Integer> g = elements(); biggest = g.next( ); while (g.hasNext()) { int z = g.next(); if (z > biggest) biggest = z; } } (continued on next slide)
public int max () throws EmptyException { if (size() == 0) throw new EmptyException("MaxIntSet.max"); return biggest; } public boolean repOk () { if (!super.repOk()) return false; if (size() == 0) return true; boolean found = false; Iterator<Integer> g = elements ( ); while (g.hasNext()) { int z = g.next(); if (z > biggest) return false; if (z == biggest) found = true; } return found; } }
Notes: • A subclass constructor must first call a super class constructor: super() • If the call of the super class constructor has no arguments, then it can be omitted (Java will add one by default) public MaxIntSet () {} // also correct • If the call of super() has arguments, then it has to be explicitly written in the subclass constructor • super can also be used to represent overridden methods • E.g., super.insert • Overridden methods are visible to subclasses, not visible to the using code • Methods insert() and remove() are implemented although not specified in the subclass
The implementation of repOk of a subclass should always check the rep invariant of the superclass • The current implementation of remove is not very efficient • It goes through els twice • els has to be accessible by MaxIntSet directly to improve the efficiency (Use protected) • However, if the rep invariant of the super class becomes accessible by its subclass, then the rep invariant of the subclass should include the rep invariant of the superclass // The rep invariant is: // I_MaxIntSet(c) = I_IntSet(c) && c.size > 0 => // ( c.biggest in AF_IntSet(c) && // for any x in AF_IntSet(c) (x <= c.biggest) )
Exception Types • All exception types are subtypes of Throwable • The implementation of Throwable provides methods to access the string within the exception object • New exception types can therefore be implemented just by defining their constructors • It is also possible to define an exception type that has additional methods and that has more information in its objects
Example: public MyException extends Exception { private int val; public MyException (String s, int v) { super(s); val = v; } public MyException (int v) { super(); val = v; } public int valueOf () { return val; } }
Abstract Classes • An abstract class provides only a partial implementation of a type • It may contain some instance variables • If it has instance variables, it will also have one or more constructors • These constructors are not visible to the using code, but they can be used by subclasses to initialize the superclass’s rep • An abstract class may contain both abstract methods and regular methods
The implementation of some normal methods may use some abstract methods • Allows the (abstract) superclass to define the generic part of the implementation, with the subclasses filling in the details • Example: SortedIntSet • Like IntSet except that the elements iterator provides access to the elements in sorted order
/** OVERVIEW: A sorted int set is an int set whose elements are accessible in sorted order. */ public class SortedIntSet extends IntSet { // EFFECTS: constructors. Makes this be the empty sorted set. */ public SortedIntSet () /** EFFECTS: Returns a generator that will produce all elements of this, each exactly once, in ascending order. REQUIRES: this not be modified while the generator is in use. */ public Iterator<Integer> elements () (continued on next slide)
/** EFFECTS: If this is empty throws EmptyException; else returns the largest element of this. */ public int max () throws EmptyException public boolean subset (SortedIntSet s) } • Notes: • Iterator elements is specified since its behavior has changed • Since the overloaded subset method has the same behavior as that of the inherited one, no specification is needed
To implement SortedIntSet efficiently, we may use an ordered list as its rep • However, if SortedIntSet is implemented as a subclass of IntSet, then the instance variables, such as els, in IntSet are no long useful • But they will still be part of a SortedIntSet object • We can obtain subtypes whose objects do not contain unused instance variables by not having them in the superclass • IntSet can be defined as an abstract class
public abstract class IntSet { protected int sz; // the size // constructors public IntSet () { sz = 0; } // abstract methods public abstract void insert (int x); public abstract void remove (int x); public abstract Iterator<Integer> elements (); public abstract boolean repOk (); (continued on next slide)
// methods public boolean isIn (int x) { Iterator<Integer> g = elements (); Integer z = new Integer(x); while (g.hasNext()) if (g.next().equals(z)) return true; return false; } public int size () { return sz; } // implementations of subset and toString ... }
Notes: • Note the keyword abstract • Methods isIn, subset and toString are implemented by using abstract methods elements • IntSet has a instance variable sz for method size. This is because: • Although size may also be implemented using elements, it is inefficient • All subclasses of IntSet need to implement size efficiently anyway
The visibility of sz should be decided • If it is private, then we need to provide protected methods to access it and rep invariant sz >= 0 can be maintained • Not interesting, since sz is to be maintained by subclasses anyway • sz is declared as protected, allowing direct accesses by subclasses for efficiency • See page 166 • repOk of IntSet is also abstract, since no rep invariant is guaranteed by IntSet • An abstract class typically has no abstraction function • Rep and implementation are in subclasses
Implementation of SortedIntSet public class SortedIntSet extends IntSet { // The abstraction function is: // AF(c) = c.els[1], . . . , c.els[c.sz] // The rep invariant is: // I(c) = c.els !=null && c.sz == c.els.size // && 0 < i < j <= c.sz-1 => // c.els[i] < c.els[j] private ArrayList<Integer> els; public SortedIntSet () { els = new ArrayList<Integer>(); } public int max () throws EmptyException { if (sz == 0) throw new EmptyException("SortedIntSet.max"); return els.get(sz - 1); } (continued on next slide)
public Iterator<Integer> elements () { return els.iterator(); } public boolean subset (IntSet s) { try { return subset((SortedIntSet) s); } catch (ClassCastException e) { return super.subset(s); } } public boolean subset (SortedIntSet s) { // implementation here takes advantage of the // ordered property of els ... } // implementations of insert, remove and repOk }
Notes: • The implementation utilizes the ArrayList<Integer> • Different from the OrderedIntList class used in the textbook • The subclass must implement all abstract methods • It may inherit concrete methods or override them for efficiency purposes • size (inherited) • subset (overridden) • Note that rep invariant must clearly state that the elements in els are sorted • Subclasses can also be abstract • Constructor super inserted by default
The implementation of the iterator method elements utilizes the iterator method of ArrayList<Integer>: return els.iterator(); • It does not implement an inner class as we did in the Iteration Abstraction lecture • Why? • If we need to implement another iterator methods desc_elements which produces all the elements in descending order, can we still use the iterator method of ArrayList<Integer>?
Interfaces • An interface contains only nonstatic, public methods • All methods are abstract • An interface is implemented by a class that has an implements clause in its header • Interfaces provide a way of defining types that have multiple supertypes • A class can only extend one class, but it can implements one or more interfaces
Multiple Implementations • Hierarchy can be used to provide multiple implementations of a type • Basically, it is a very constrained type family, in which all members have exactly the same methods and behavior • E.g., the sparse and dense implementations of Poly • The supertype in such a situation is defined as either an interface or an abstract class • Defer implementation details to subclasses • Subclasses will provide exactly the same behavior defined by the specification of the supertype • Plus subclass constructors
The implementations of subclasses are largely invisible to users • The only place where users need to know them is when they need to the call the constructor of the appropriate subclass • Example: Lists • Consider an IntList abstraction • Use different implementations for the empty list and non-empty lists
/** OVERVIEW: IntLists are immutable lists of Objects. A typical IntList is a sequence [x1, ..., xn]. */ public abstract class IntList { /** EFFECTS: If this is empty throws EmptyException; else returns first element of this. */ public abstract Object first () throws EmptyException; /** EFFECTS: If this is empty throws EmptyException; else returns the list containing all but the first element of this, in the original order. */ public abstract IntList rest () throws EmptyException; (continued on next slide)
/** EFFECTS: Returns a generator that will produce the elements of this, each exactly once, in their order in this. */ public abstract Iterator<Object> elements (); /** EFFECTS: Returns a new list that has x as the first element and this as the rest. */ public abstract IntList addEl (Object x); /** EFFECTS: Returns a count of the number of elements of this. */ public abstract int size (); public abstract boolean repOk (); public String toString () public boolean equals (IntList o) }
Implementation of IntList /** Overview: IntLists are immutable lists of Objects. A typical IntList is a sequence [xi, ..., xn]. */ public abstract class IntList { // abstract methods public abstract Object first () throws EmptyException; public abstract IntList rest () throws EmptyException; public abstract Iterator elements (); public abstract IntList addEl (Object x); public abstract int size (); public abstract boolean repOk (); (continued on next slide)
// methods public String toString () { ... } public boolean equals (Object o) { try { return equals ((IntList) o); } catch (ClassCastException e) { return false; } } public boolean equals (IntList o) { // compare elements using elements iterator } } • No constructor since we have no rep • toString and equals are implemented using the iterator elements
The overriding equals method can also be written as follows: public boolean equals (Object o) { if (o instanceof IntList) return equals ((IntList) o); else return false; } • Is ClassCastException a checked exception or an unchecked one?
Implementation of the empty list public class EmptyIntList extends IntList { public EmptyIntList () {} public Object first () throws EmptyException { throw new EmptyException("EmptyIntList.first"); } public IntList rest () throws EmptyException { throw new EmptyException("EmptyIntList.rest"); } public IntList addEl (Object x) { return new FullIntList(x); } public boolean repOk () { return true; } public String toString () { return "IntList: Empty"; } public int size () { return 0; } (continued on next slide)