Reflection and Generics

   


The Class class is now generic. For example, String.class is actually an object (in fact, the sole object) of the class Class<String>.

The type parameter is useful because it allows the methods of Class<T> to be more specific about their return types. The following methods of Class<T> take advantage of the type parameter:

 T newInstance() T cast(Object obj) T[] getEnumConstants() Class<? super T> getSuperclass() Constructor<T> getConstructor(Class... parameterTypes) Constructor<T> getDeclaredConstructor(Class... parameterTypes) 

The newInstance method returns an instance of the class, obtained from the default constructor. Its return type can now be declared to be T, the same type as the class that is being described by Class<T>. That saves a cast.

The cast method returns the given object, now declared as type T if its type is indeed a subtype of T. Otherwise, it throws a BadCastException.

The getEnumConstants method returns null if this class is not an enum class or an array of the enumeration values, which are known to be of type T.

Finally, the getConstructor and getdeclaredConstructor methods return a Constructor<T> object. The Constructor class has also been made generic so that its newInstance method has the correct return type.


 java.lang.Class<T> 1.0 

  • T newInstance() 5.0

    returns a new instance constructed with the default constructor.

  • T cast(Object obj) 5.0

    returns obj if it is null or can be converted to the type T, or throws a BadCastException otherwise.

  • T[] getEnumConstants() 5.0

    returns an array of all values if T is an enumerated type, null otherwise.

  • Class<? super T> getSuperclass() 5.0

    returns the superclass of this class, or null if T is not a class or the class Object.

  • Constructor<T> getConstructor(Class... parameterTypes) 5.0

  • Constructor<T> getDeclaredConstructor(Class... parameterTypes) 5.0

    get the public constructor, or the constructor with the given parameter types.


 java.lang.reflect.Constructor<T> 1.1 

  • T newInstance(Object... parameters) 5.0

    returns a new instance constructed with the given parameters.

Using Class<T> Parameters for Type Matching

It is sometimes useful to match the type variable of a Class<T> parameter in a generic method. Here is the canonical example:

public static <T> Pair<T> makePair(Class<T> c) throws InstantiationException, IllegalAccessException { return new Pair<T>(c.newInstance(), c.newInstance()); }

If you call

 makePair(Employee.class) 

then Employee.class is an object of type Class<Employee>. The type parameter T of the makePair method matches Employee, and the compiler can infer that the method returns a Pair<Employee>.

Generic Type Information in the Virtual Machine

One of the notable features of Java generics is the erasure of generic types in the virtual machine. Perhaps surprisingly, the erased classes still retain some faint memory of their generic origin. For example, the raw Pair class knows that it originated from the generic class Pair<T>, even though an object of type Pair can't tell whether it was constructed as a Pair<String> or Pair<Employee>.

Similarly, consider a method

 public static Comparable min(Comparable[] a) 

that is the erasure of a generic method

 public static <T extends Comparable<? super T>> T min(T[] a) 

You can use the reflection API enhancements of JDK 5.0 to determine that

  • the generic method has a type parameter called T;

  • the type parameter has a subtype bound that is itself a generic type;

  • the bounding type has a wildcard parameter;

  • the wildcard parameter has a supertype bound; and

  • the generic method has a generic array parameter.

In other words, you get to reconstruct everything about generic classes and methods that their implementors declared. However, you won't know how the type parameters were resolved for specific objects or method calls.

NOTE

The type information that is contained in class files to enable reflection of generics is incompatible with older virtual machines.


In order to express generic type declarations, JDK 5.0 provides a new interface Type in the java.lang.reflect package. The interface has the following subtypes:

  • the Class class, describing concrete types

  • the TypeVariable interface, describing type variables (such as T extends Comparable<? super T>)

  • the WildcardType interface, describing wildcards (such as ? super T)

  • the ParameterizedType interface, describing generic class or interface types (such as Comparable<? super T>)

  • the GenericArrayType interface, describing generic arrays (such as T[])

Figure 13-5 shows the inheritance hierarchy. Note that the last four subtypes are interfaces the virtual machine instantiates suitable classes that implement these interfaces.

Figure 13-5. The Type class and its descendants


Example 13-4 uses the generic reflection API to print out what it discovers about a given class. If you run it with the Pair class, you get this report:

 class Pair<T extends java.lang.Object> extends java.lang.Object public T extends java.lang.Object getSecond() public void setFirst(T extends java.lang.Object) public void setSecond(T extends java.lang.Object) public T extends java.lang.Object getFirst() 

If you run it with ArrayAlg in the PairTest2 directory, the report displays the following method:

public static <T extends java.lang.Comparable> Pair<T extends java.lang.Comparable> minmax (T extends java.lang.Comparable[])

The API notes at the end of this section describe the methods used in the example program.

Example 13-4. GenericReflectionTest.java
   1. import java.lang.reflect.*;   2. import java.util.*;   3.   4. public class GenericReflectionTest   5. {   6.    public static void main(String[] args)   7.    {   8.       // read class name from command-line args or user input   9.       String name;  10.       if (args.length > 0)  11.          name = args[0];  12.       else  13.       {  14.          Scanner in = new Scanner(System.in);  15.          System.out.println("Enter class name (e.g. java.util.Date): ");  16.          name = in.next();  17.       }  18.  19.       try  20.       {  21.          // print generic info for class and public methods  22.          Class cl = Class.forName(name);  23.          printClass(cl);  24.          for (Method m : cl.getDeclaredMethods())  25.             printMethod(m);  26.       }  27.       catch (ClassNotFoundException e)  28.       {  29.          e.printStackTrace();  30.       }  31.    }  32.  33.    public static void printClass(Class cl)  34.    {  35.       System.out.print(cl);  36.       printTypes(cl.getTypeParameters(), "<", ", ", ">");  37.       Type sc = cl.getGenericSuperclass();  38.       if (sc != null)  39.       {  40.          System.out.print(" extends ");  41.          printType(sc);  42.       }  43.       printTypes(cl.getGenericInterfaces(), " implements ", ", ", "");  44.       System.out.println();  45.    }  46.  47.    public static void printMethod(Method m)  48.    {  49.       String name = m.getName();  50.       System.out.print(Modifier.toString(m.getModifiers()));  51.       System.out.print(" ");  52.       printTypes(m.getTypeParameters(), "<", ", ", "> ");  53.  54.       printType(m.getGenericReturnType());  55.       System.out.print(" ");  56.       System.out.print(name);  57.       System.out.print("(");  58.       printTypes(m.getGenericParameterTypes(), "", ", ", "");  59.       System.out.println(")");  60.    }  61.  62.    public static void printTypes(Type[] types, String pre, String sep,  String suf)  63.    {  64.       if (types.length > 0) System.out.print(pre);  65.       for (int i = 0; i < types.length; i++)  66.       {  67.          if (i > 0) System.out.print(sep);  68.          printType(types[i]);  69.       }  70.       if (types.length > 0) System.out.print(suf);  71.    }  72.  73.    public static void printType(Type type)  74.    {  75.       if (type instanceof Class)  76.       {  77.          Class t = (Class) type;  78.         System.out.print(t.getName());  79.       }  80.       else if (type instanceof TypeVariable)  81.       {  82.          TypeVariable t = (TypeVariable) type;  83.          System.out.print(t.getName());  84.          printTypes(t.getBounds(), " extends ", " & ", "");  85.       }  86.       else if (type instanceof WildcardType)  87.       {  88.          WildcardType t = (WildcardType) type;  89.          System.out.print("?");  90.          printTypes(t.getLowerBounds(), " extends ", " & ", "");  91.          printTypes(t.getUpperBounds(), " super ", " & ", "");  92.       }  93.       else if (type instanceof ParameterizedType)  94.       {  95.          ParameterizedType t = (ParameterizedType) type;  96.          Type owner = t.getOwnerType();  97.          if (owner != null) { printType(owner); System.out.print("."); }  98.          printType(t.getRawType());  99.          printTypes(t.getActualTypeArguments(), "<", ", ", ">"); 100.       } 101.       else if (type instanceof GenericArrayType) 102.       { 103.          GenericArrayType t = (GenericArrayType) type; 104.          System.out.print(""); 105.          printType(t.getGenericComponentType()); 106.          System.out.print("[]"); 107.       } 108. 109.    } 110. } 


 java.lang.Class<T> 1.0 

  • TypeVariable[] getTypeParameters() 5.0

    gets the generic type variables if this type was declared as a generic type, or an array of length 0 otherwise.

  • Type getGenericSuperclass() 5.0

    gets the generic type of the superclass that was declared for this type, or null if this type is Object or not a class type.

  • Type[] getGenericInterfaces() 5.0

    gets the generic types of the interfaces that were declared for this type, in declaration order, or an array of length 0 if this type doesn't implement interfaces.


 java.lang.reflect.Method 1.1 

  • TypeVariable[] getTypeParameters() 5.0

    gets the generic type variables if this method was declared as a generic method, or an array of length 0 otherwise.

  • Type getGenericReturnType() 5.0

    gets the generic return type with which this method was declared.

  • Type[] getGenericParameterTypes() 5.0

    gets the generic parameter types with which this method was declared. If the method has no parameters, an array of length 0 is returned.


 java.lang.reflect.TypeVariable 5.0 

  • String getName()

    gets the name of this type variable.

  • Type[] getBounds()

    gets the subclass bounds of this type variable, or an array of length 0 if the variable is unbounded.


 java.lang.reflect.WildcardType> 5.0 

  • Type[] getLowerBounds()

    gets the subclass (extends) bounds of this type variable, or an array of length 0 has no subclass bounds

  • Type[] getUpperBounds()

    gets the superclass (super) bounds of this type variable, or an array of length 0 has no superclass bounds.


 java.lang.reflect.ParameterizedType 5.0 

  • Type getRawType()

    gets the raw type of this parameterized type.

  • Type[] getActualTypeArguments()

    gets the type parameters with which this parameterized type was declared.

  • Type getOwnerType()

    gets the outer class type if this is an inner type, or null if this is a top-level type.


 java.lang.reflect.GenericArrayType 5.0 

  • Type getGenericComponentType()

    gets the generic component type with which this array type was declared.

You have now reached the end of the first volume of Core Java. This volume covered the fundamentals of the Java programming language and the parts of the standard library that you need for most programming projects. We hope that you enjoyed your tour through the Java fundamentals and that you found useful information along the way. For advanced topics, such as networking, multithreading, security, and internationalization, please turn to the second volume.


       
    top



    Core Java 2 Volume I - Fundamentals
    Core Java(TM) 2, Volume I--Fundamentals (7th Edition) (Core Series) (Core Series)
    ISBN: 0131482025
    EAN: 2147483647
    Year: 2003
    Pages: 132

    flylib.com © 2008-2017.
    If you may any questions please contact us: flylib@qtcs.net