Section 7.1. Generics for Reflection


7.1. Generics for Reflection

Java has supported facilities for reflection since version 1.0 and class literals since version 1.1. Central to these is the class Class, which represents information about the type of an object at run time. You may write a type followed by .class as a literal that denotes the class token corresponding to that type, and the method getClass is defined on every object and returns a class token that represents the reified type information carried by that object at run-time. Here is an example:

 Class ki = Integer.class; Number n = new Integer(42); Class kn = n.getClass(); assert ki == kn; 

For a given class loader, the same type is always represented by the same class token. To emphasize this point, here we compare class tokens using identity (the == operator). However, in practice, it often is more robust to use equality (the equals method).

One of the changes in Java 5 is that the class Class now takes a type parameter, so Class<T> is the type of the class token for the type T. The preceding code is now written as follows:

 Class<Integer> ki = Integer.class; Number n = new Integer(42); Class<? extends Number> kn = n.getClass(); assert ki == kn; 

Class tokens and the getClass method are treated specially by the compiler. In general, if T is a type without type parameters, then T.class has type Class<T>, and if e is an expression of type T then e.getClass() has type Class<? extends T>. (We'll see what happens when T does have type parameters in the next section.) The wildcard is needed because the type of the object referred to by the variable may be a subtype of the type of the variable, as in this case, where a variable of type Number contains an object of type Integer.

For many uses of reflection, you won't know the exact type of the class token (if you did, you probably wouldn't need to use reflection), and in those cases you can write Class<?> for the type, using an unbounded wildcard. However, in some cases the type information provided by the type parameter is invaluable, as in the variant of toArray that we discussed in Section 6.5:

 public static <T> T[] toArray(Collection<T> c, Class<T> k) 

Here the type parameter lets the compiler check that the type represented by the class token matches the type of the collection and of the array.

Further Examples of Generics for Reflection The class Class<T> contains just a few methods that use the type parameter in an interesting way:

 class Class<T> {   public T newInstance();   public T cast(Object o);   public Class<? super T> getSuperclass();   public <U> Class<? extends U> asSubclass(Class<U> k);   public <A extends Annotation> A getAnnotation(Class<A> k);   public boolean isAnnotationPresent(Class<? extends Annotation> k);   ... } 

The first returns a new instance of the class, which will, of course, have type T. The second casts an arbitrary object to the receiver class, and so it either throws a class cast exception or returns a result of type T. The third returns the superclass, which must have the specified type. The fourth checks that the receiver class is a subclass of the argument class, and either throws a class cast exception or returns the receiver with its type suitably changed.

The fifth and sixth methods are part of the new annotation facility. The methods are interesting, because they show how the type parameter for classes can be used to good effect. For example, Retention is a subclass of Annotation, so you can extract the retention annotation on a class k as follows:

 Retention r = k.getAnnotation(Retention.class); 

Here the generic type gains two advantages. First, it means that no cast is required on the result of the call, because the generic type system can assign it precisely the correct type. Second, it means that if you accidentally call the method with a class token for a class that is not a subclass of Annotation, then this is detected at compile time rather than at run time.

Another use of class tokens, similar to that for annotations, appears in the getListeners method of the class Component in the package java.awt:

 public <T extends EventListener>   T[] getListeners(Class<T> listenerType); 

Again, this means that the code of getListeners requires no cast, and it means that the compiler can check that the method is called with a class token of an appropriate type.

As a final example of an interesting use of class tokens, the convenience class Collections contains a method that builds a wrapper that checks whether every element added to or extracted from the given list belongs to the given class. (There are similar methods for other collection classes, such as sets and maps.) It has the following signature:

 public static <T> List<T> checkedList(List<T> l, Class<T> k) 

The wrapper supplements static checking at compile time with dynamic checking at run time, which can be useful for improving security or interfacing with legacy code (see Section 8.1). The implementation calls the method cast in the class Class described earlier, where the receiver is the class token passed into the method, and the cast is applied to any element read from or written into the list using get, set, or add. Yet again, the type parameter on Class<T> means that the code of checkedList requires no additional casts (beyond calls to the cast method in the class Class), and that the compiler can check that the method is called with a class token of an appropriate type.




Java Generics and Collections
Java Generics and Collections
ISBN: 0596527756
EAN: 2147483647
Year: 2006
Pages: 136

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