COMP 310
|
Lec20: Generic List Frameworks |
![]() ![]() ![]() ![]() |
For today's discussion, we will examine how generics can be used to reformulate our immutable list framework to produce a much more type-safe system.
The code available for download (genILists.zips) contains the algorithms mentioned below and test code plus the list implementation of a mutable list framework (LRS).
Here's the code:
The definition of visitors in the generic IList framework is of particular interest: It shows the use of paremeterized methods, upper and lower bounds and multiple type parameters very well.
Here is the definition of the IList
interface:
package listFW; /** * Defines an immutable list that holds elements of type E. * Serves as a host for an algorithm on a list to visit its internal structure. * Has a "hook" method to call on the appropriate method of the visitor, making * the immutable list structure a framework. * @author Dung X. Nguyen * @author Stephen B. Wong * @since Copyright 2004 - DXN, SBW All rights reserved * @stereotype host */ public interface IList<E> { /** * A visitor pattern "hook" method that executes anIListAlgo
. * @param algo the visitor, the algorithm to be executed. * Any visitor that works on a super type of E is accepted. * @param inp a generic input parameter to be used by the algorithm algo. * @returnObject
output from executing the algorithm algo. */ public abstract <R,P> R execute(IListAlgo<? super E, R, P> algo, P ... inp); }
First, note the <E>
in the interface declaration: It tells you that we are defining
a list containing instances of E
, whatever that may be. For example, an IList<Integer>
contains Integer
objects.
In the definition of the execute()
method, however, we see <R,P>
, and that defines
two more type parameters R
and P
that are only valid for this one method. This is an example
of a parameterized method inside a parameterized class. R
is the type of the return value of the execute()
method, and P
is the type of the inp
varargs.
Furthermore, the definition of execute()
spells out exactly what kind of visitor it needs:
E
, or supertypes of it. Supertypes
are allowed too, because a visitor that is designed to handle any object of type Number
can certainly also
handle objects or type Integer
. We therefore make use of lower bounds.R
.P
as parameters.The actual types of R
and P
will be deduced at the call site for execute()
;
they are not nailed down in this definition. The compiler, however, will make sure that all occurrences of R
,
P
and E
at a call site are consistent, so you cannot execute a visitor for lists of numbers
on a list of strings, or pass string varargs to a visitor requiring numbers.
IListAlgo
interface:
package listFW; /** * A visitor to (algorithm on) an IList<T> where T is a subclass of E * Also parameterized by it return type R and parameter type T. */ public interface IListAlgo<E,R,P> { public abstract R emptyCase(IMTList<? extends E> host, P ... inp); public abstract R nonEmptyCase(INEList<? extends E> host, P ... inp); }
The visitor has three type parameters, E
, R
and P
, and they specify
the types of the list elements the visitor can process, the return value, and the varargs, respectively. If you
look at the definition of the two visitor methods, you can see that they accept IMTList
and INEList
instances whose type parameter is E
or a subclass thereof. This is an example of an upper bound.
Examples of algorithms on the generic IList framework
This page was adapted from discussion material created by Mathias Ricken (code created by S. Wong and D. X. Nguyen)
© 2010 by Stephen Wong