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
java.lang.reflect.Constructor<T> 1.1
Using Class<T> Parameters for Type MatchingIt 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 MachineOne 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
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
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:
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 descendantsExample 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.java1. 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
java.lang.reflect.Method 1.1
java.lang.reflect.TypeVariable 5.0
java.lang.reflect.WildcardType> 5.0
java.lang.reflect.ParameterizedType 5.0
java.lang.reflect.GenericArrayType 5.0
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. |