As observed earlier, changes to a generic class (such as adding a method or a parameter to a method) can require explicit changes to all its ``instantiation'' subclasses. To minimize the impact of such changes, the Reflection API can be used. This is illustrated by the following Dictionary class example that is ``parameterized'' with respect to a linear order class as follows. (We have included the details for the interested readers; this example can be safely skipped without adversely affecting the readability of the rest of the paper.)
import java.util.*;
import java.lang.reflect.*;
class Dictionary {
private Class orderClass;
private Method lessThan, equal;
private Vector vec = new Vector();
public Dictionary(Class cl) {
orderClass = cl; /* generic parameter */
try {
lessThan = cl.getMethod("lessThan", new Class[]{cl});
equal = cl.getMethod( "equal" , new Class[]{cl});
} catch (Exception e) {System.out.println(e);}
}
public void insert(Object o) {
int i;
if (orderClass.isInstance(o)) {
try {
for (i = 0; i < vec.size(); i++) {
if ( auxInvoker( lessThan, o, new Object[]{vec.elementAt(i)}) ) break;
}
vec.insertElementAt(o,i);
} catch (Exception e) {System.out.println(e);}
}
}
public Object member(Object o) {
if (orderClass.isInstance(o)) {
try {
for (int i = 0; i < vec.size(); i++) {
if ( auxInvoker( equal, o, new Object[]{vec.elementAt(i)}) ) return o;
if ( auxInvoker( lessThan, o, new Object[]{vec.elementAt(i)}) ) break;
}
} catch (Exception e) {System.out.println("*2* " + e);}
}
return null;
}
private static boolean auxInvoker(Method m, Object o, Object[] arr)
throws IllegalAccessException,
IllegalArgumentException, InvocationTargetException {
Boolean b = (Boolean) m.invoke(o, arr);
return b.booleanValue();
}
}
The above class can be used to implement a Dictionary of strings as follows.
class EGLinearOrder {
String s;
EGLinearOrder(String s) {
this.s = s;
}
public boolean lessThan(EGLinearOrder t) {
return (s.compareTo(t.s) == -1);
}
public boolean equal(EGLinearOrder t) {
return (s.compareTo(t.s) == 0);
}
public String toString() {
return ( "Instance of EGLinearOrder with field s = " + s );
}
}
public class ReflectGenerics {
public static void main (String[] args) {
EGLinearOrder o = new EGLinearOrder("Jill");
EGLinearOrder p = new EGLinearOrder("Jack");
Dictionary c = new Dictionary(p.getClass());
c.insert(o);
c.insert(p);
System.out.println("Result ---> " + (EGLinearOrder) c.member(p));
}
}
Even though the code for the generic Dictionary class using reflection looks complicated (in fact even downright ``ugly''), the code for instantiation is straightforward. Unlike the non-reflective solution presented first, changes to the Dictionary class -- such as adding a method public void delete(Object o) ... -- do not require explicit changes to its instantiations. However, this approach cannot be used to instantiate generic parameters to primitive types, and will lose out on compile-time error checks1. It is also clearly slower than the non-reflective simulation.