next up previous
Next: 2.3 Approximating Multiple Inheritance Up: 2.2 Approximating Generics in Previous: 2.2.2 Using interface/class parameters

   
2.2.3 Using Java Reflection

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.


next up previous
Next: 2.3 Approximating Multiple Inheritance Up: 2.2 Approximating Generics in Previous: 2.2.2 Using interface/class parameters
T. K. Prasad