CS210 Feb. 9: collect hw1, questions on pa1?
Using Comparable<T> in methods
Recall from last time our example of Comparable<Shape> over
the Shape hierarchy: it was easy to implement it once and for all
in the base class, and then it was usable for all the subclasses, that
is, we could say "Circle implements Comparable<Shape>", and do
circle.compareTo(sqr), etc, to compare areas. Comparison by area
is the "natural order" for these Shape objects.
There's only one problem: because of Java parametrized type
rules, we can't say "Circle implements Comparable<Circle>" from
this setup, because A IS-A B does NOT imply Comparable<A> IS-A
Comparable<B>. This only bothers us when we want to write
generalized binarySearch or findMax algorithms, or othe generic
algorithms, but we need to be able to do that.
Why is the setup for findMax, pg. 135, so complicated?
Answer, because there are two types here involved in the type setup, AnyType and Comparable, and one is itself parametrized.
This is the same situation as binarySearch, so it is important.
Java has to come up with some syntax to describe the relationship between AnyType and Comparable<>..
But remember, if AnyType is Square, implementing Comparable<Shape>, we can't say "Square implements Comparable<Square>" even though compareTo is fully functional for Squares.
Java lets us wildcard the usable Comparable<T>'s here. We can ask to use Comparable<ST> for any class or interface ST super to T in the hierarchy, i.e., where T IS-A ST.
This is always logically justified: if T IS-A ST, and implements Comparable<ST>, then it must implement compareTo<ST>, and this must accept a T, since T IS-A ST.
Now let's tackle this example of generic code:
public static
<AnyType extends Comparable<? super
AnyType>> int binarySearch(AnyType [] a, AnyType x)
{
... same code except
a[mid] < x is now a[mid].compareTo(x)
< 0, etc.
}
Decoding this method declaration:
First locate the left parenthesis, an important signpost. The method's parameters are to its right, until the ).
Then locate method name: binarySearch, always just before the left parenthesis.
Then locate the method return type, int, listed just before the method name.
Then any <...> "type bound", to the left of the return type. Match up <>'s to get the whole thing.
To its left, various possible descriptive keywords.
The type bound sets up the parametrization for the method.
Here we have <AnyType extends Comparable<? super AnyType>>
dummy type var Comparable<T>, where T is at
or above AnyType in its hierarchy
"extends"
here means extends or implements (see pp 134-135)
AnyType implements
Comparable<something at or super to AnyType>
Ex: Circle implement Comparable<Shape>
OK we
now see that it's a workaround for "stupid" non-compatibility of parametrized types.
This is saying: it's OK if Circle implements Comparable<Circle>,
but it's also OK if Circle implements Comparable<Shape>.
Either way, this method is willing to accept the array and do its
work.
Also see
the links under Resources at the end of the class web page to a
Canadian overview of generics that is really hard on Sun's decisions
for this ugly syntax.
If a type hierarchy is in the comparing business, the
normal thing would be to have the
base class
like Shape be Comparable<Shape>, with appropriate
compareTo
methods in subclasses if necessary. So the whole hierarchy is
Comparable, as we saw implemented for Shape in the handout.
Note: a subclass that does override compareTo
has to handle not only is own subtype, but also other subtypes, so in
the Shape case, Square.compareTo(Shape x) has to do something with an x
that is not a Square. It can throw ClassCastException, but
must make
sure that the behavior is symmetric: if
y.compareTo(x) throws, then
so must x.compareTo(y). You can see that doing compareTo
correctly
over a type hierarchy is difficult unless you can do it once and for
all in the base class.
If only a subclass T is Comparable<T>, not
the base class, this setup for binarySearch still works for searching
an array of T's. The wildcard expression is saying either
setup is OK.
Actually calling binarySearch or findMax. Suppose findMax is in class MyUtils
Shape [ ] shapes = { new Circle( 0.5 ), new Rectangle( 1.0, 3.0 ),
new Square(
2.0 ) };
System.out.println("compareTo returned " + shapes[0].compareTo(a[1]));
Shape maxOne = MyUtils.findMax(shapes);
I expected to find binarySearch as on pg. 185 in the Arrays class of
the JDK, but its not there. The related version with the
Comparator is there. So again we have to imagine this in MyUtils.
int spot = MyUtils.binarySearch(shapes, new Rectangle(4.0, 1.0));
should return spot = 2, since the area of 4 matches. Note that
shapes is ordered by area, and thus is in "natural order" as described
by its Comparator..
We'll have to cover Comparators eventually, but let's leave types for now.
Chap. 6 Collection classes
and interfaces
Containers—Lists, Sets,
Stacks, Queues, Maps—ways to hold objects, with a specific
order or not, with certain ways to get at them
Will work with JDK concrete classes ArrayList,
LinkedList, TreeSet, HashSet, TreeMap, HashMap, others
Containers and Iterators—can skip to from pg. 204 to Fig. 6.8, see the resulting model if you want.
Iterators help us scan through
objects in a container
Can iterate through an array
v—pg. 204
for
(int i = 0; i < v.length; i++)
do something with v[i]
for
(ElementType x: v)
do something with x
and you know how to do this
with an ArrayList<ElementType> al—
for
(int i = 0; i < al.size(); i++)
do something with al.get(i)
for
(ElementType x: al)
do something with x
Note v[i] and al.get(i) are both O(1)
in execution time—access to an array element.
Collection interface, pg. 211, parametrized by <AnyType> the
element types. Think of as generalization of ArrayList<T>.
Iterator interface—pg. 212-213 , parametrized by AnyType: where AnyType is the element type of the Collection.
boolean
hasNext(); // the collection has a
“next” object
AnyType next();
// get a ref to the next element object promised by hasNext, with the appropriate type
void remove(); // ignore
for now