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.