Liskov 8

advertisement
Polymorphism
Liskov 8
Outline

equals()


Revisiting Liskov’s mutable vs. not rule
Polymorphism

Uniform methods for different types


Element subtype approach


“easy” polymorphism
Planning ahead
Related subtype approach

Reacting after the fact
A word about equals


Problem:
We want to check if two objects are equal to
each other
Many ways to do so:



Object identity [A==B] (same object)
Object state [A.counter = B.counter] (similar
objects)
Object property [A.area() = B.area()]
(practically same)
Overriding equals


Object class equals is ‘==‘ check
Overriding equals means providing a
check other than object identity.


Overriding equals in a mutable class


Usually it provides object state check
A.equals(B) is true/false at different times
Immutable classes don’t suffer from this
problem
How to get in trouble: Storing
mutable types in collections


Assume a collection that does not allow
duplicates (eg java.util.TreeSet)
Aim: to store mutable types with
overridden equals.
void insert (Object x) {
for all elements in collection{
if (element[i].equals(x)) return; // no duplicates
}
collection.addElement(x);
What’s the Problem?
Consider client code for fig 8.1:
Set s = new HashSet(); // AF(s) = {}
Vector x = new Vector() // AF(x) = []
Vector y = new Vector() // AF(y) = []
s.insert(x);
// AF(s) = {[]}
s.insert(y);
// AF(s) = {[], []}? Or {[]}?
s.contains(y)
// true or false?
y.add(“cat”);
// AF(y) = [“cat”]
// AF(s) = ?????
s.contains(y);
// true or false?
s.insert(y);
// s.state = {[], [“cat”]}???
y.remove(“cat”);
// s.state = {[], []} ??? !!!!
Liskov’s Solution

Liskov’s approach to equals() avoids this
problem


Mutable objects compared via “==“
A workaround for Java’s decision
public boolean equals (Object x) {
if (!x instanceOf Container) return false;
return (el == ((Container) x.el));
}



Equality does not pose a problem anymore!
We can insert both x and y in s.
Even if x modified, we will still find y in s
Outline

equals()


Revisiting Liskov’s mutable vs. not rule
Polymorphism

Uniform methods for different types


Element subtype approach


“easy” polymorphism
Planning ahead
Related subtype approach

Reacting after the fact
What is Polymorphism?

Generalize abstractions


They should work for many types
E.g.: IntSet could be generalized to Set



Not just store integers, but other data types
Saves us from creating new data
abstractions for each data type (like
PolySet, FloatSet, etc.)
Compare IntSet with HashSet, TreeSet
Polymorphic procedures



Procedures can be polymorphic with respect
to types of arguments
E.g.: Intset.insert(int x) becomes
Set.Insert(Object x) or overloaded
Set.Insert(…) with the specified list of types
How does this affect specs of procedures?
Polymorphic Data abstractions

Two kinds:

element subtype (Comparable, Addable)



related subtype (Comparator, Adder)



Pre planning.
Unique way for all subtypes
post planning, class designer did not provide it
create a related type for each object type
Both kinds use interfaces for generalization
Comparable Interface (fig 8.4)
public interface Comparable <T> {
//O: Subtypes of Comparable provide a method to
determine the ordering of their objects. This ordering
must be a total order over their objects, and it should
be both transitive and antisymmetric. Furthermore,
x.compareTo(y)== 0 implies (iff???) x.equals(y).
public int compareTo (T x) throws CCE, NPE;
//E: If x is null, throws NPE; if this and x aren’t
compatible, throws CCE. Otherwise, if this is less
than x returns <0; if this equals x, returns 0 and if
this is greater than x, returns >0
OrderedList (Figure 8.5)


Stores elements which implement
Comparable interface
Bug in addEl() (first line)




“if (val == null)” should be “if (el == null)”
Specs: order of exceptions!
Very similar to TreeSet
What is the abstract state?
Ordered List code (fig 8.5)
public class OL {
private boolean empty; private OL left, right;
private Comparable val;
public void addEl(Comparable el) throws NPE,DE,CCE
// M: this // E: if el is null throw NPE else if el is in this throw DE
else if el is incomparable to elements in this throw CCE else add
el to this
if (el == null) throw new NPE(...)
if (empty) {left = new OL(); right = new OL(); val = el; empty =
false; return;}
int n = el.compareTo(val);
if (n == 0) throw new DE(...);
if(n < 0) left.addEl(el); else right.addEl(el); }
Related subtype approach





After classes have been designed
We want a collection to store and
operate on any of such types
Some client code may already exist! We
don’t want it to break.
So we create related subtype
Accompanies each type, supports
desired operations
Related subtype

Example problem (figure 8.8):
We want to sum up all the elements in
a set. SumSet class must maintain a
running sum of all Integers, Floats or
Poly’s stored.



We store one type of object at a time
SumSet a  stores only Polys
SumSet b  stores only Integers
SumSet Implementation (Fig 8.8)
public class SumSet {
private Vector els; private Object s; private Adder a;
public SumSet(Adder p) throws NPE {
els = new Vector(); a = p; s = p.zero();}
public void insert(Object x) throws NPE, CCE {
// M: this // E: if x is null throw NPE; if x cannot be added to this
// throw CCE; else adds x to this and adjusts the sum
Object z = a.add(s, x);
if (!els.contains(x)) {
els.add(x); s = z;
}
public Object sum() { //E: return sum of elements in this
return s; }
}

Note order of exceptions

What’s an “Adder”?
Comparator interface
public interface Comparator <T> {
public int compare (T x, T y) throws NPE, CCE;
//E: IF x,y = null, throws NPE;
// If x and y are not comparable, throws CCE
// If x less than y, returns -1; if x is equal to y, returns 0; if x
greater than y, returns 1
}


Why two parameters in compare()?
How does client use it?
StringFromBackComparator




String.compareTo(String) method provides a
dictionary like ordering. (lexicographical
ordering)
What if we want a different ordering?
For example: We want to compare strings
from back. “cat”.comparison(“dog”) should
return 1
We can achieve so by implementing our own
Comparator: StringFromBackComparator
(SFBC)
SFBC implementation
public class SFBC implements Comparator<String> {
public int compare (String x, String y){
if (x==null || y == null) throw new NPE();
… //compare sx and sy from back..
}
}
How does client use SFBC?
String n = “cat”;
String p = “dog”;
int m = (new SFBC()).compare(n,p);
Or
Set<String> set = new TreeSet<String> (new SFBC());
Iterator<String> itr = set.iterator();
// AF (itr) = [dog, cat]
E.g.: ReverseComparator




We are not satisfied by
comparable.compareTo() method.
We cannot change it!
Alternate way: use Comparator to
define our own criteria 
Here, we want to reverse the evaluation
of Comparable.compareTo
Implementation
public class RC<T extends Comparable<T>> implements
Comparator<T> {
//O: Reverse the natural order of elements. Eg: 7<3 here
public int compare (T x, T y) throws NPE, CCE
{
return –x.compareTo(y);
}
How about absolute
comparison?
public class AbsoluteComparator implements Comparator<Integer>
//O: Compare on absolute value of (Integer) elements
public int compare (Integer a, Integer b) throws NPE, CCE
{
if (a < 0) a = -a; if (b < 0) b = -b; // absolute values
if (a < b) return -1;
if (a > b) return 1;
return 0;
}

Is this correct?
Similarities between
Comparable and Addable




Comparable
Provides uniform way
to compare elements
Abstracts from types
All types compared in a
similar manner




Addable
Provides uniform way
to add elements
Abstracts from types
All types added in a
similar manner
Download