Creating New Objects and the Constructor Class
Inner Class Constructors
You can use a Class object’s newinstance method to create a new instance (object) of the type it represents. This method invokes the class’s no-arg constructor and returns a reference to the newly created object. For a class object of type Class the returned object is of type T. Creating a new object in this way is useful when you want to write general code and let the user specify the class. For example, you could modify the general sorting algorithm tester from “Designing a Class to Be Extended” on page 108 so that the user could type the name of the class to be tested and use that as a parameter to the forname lookup method. Assuming that the given class name was valid, newinstance could then be invoked to create an object of that type. Here is a new main method for a general testsort class:
So, for example, the typevariable objects Method.gettypeparameters returns would be of type typevariable. Each type variable has a name returned by getname. It also has one or more upper bounds, obtained as a Type[] from getbounds. Recall that if there is no explicit upper bound then the upper bound is Object. The getgenericdeclaration method returns the Type object for the genericdeclaration in which the typevariable was declared. For example, the expression typevariable.class.gettypeparameters()[0] would yield a typevariable object that represents D in the declaration above. If you invoked getgenericdeclaration on this object, it would return the Class object for the typevariable interface. More generally, for any genericdeclaration object g with at least one type parameter,
Parameterized Types
The getownertype method (which perhaps would have been better called getdeclaringtype) returns the Type object for the type in which this parameterizedtype object is a member. If this is not a member of another type then null is returned. This is analogous to the Class.getdeclaringclass method except that a Type object is returned. Like typevariable objects, parameterizedtype objects are created on demand and are not always the same object, so you should use equals not == to check for equivalent parameterized type objects. Also, when a parameterized type is created, all its type arguments are also created, and this applies recursively. Both of the above methods will sometimes throw either typenotfoundexception or malformedparameterizedtypeexception. Finally, parameterizedtype also has the method getrawtype, which returns the Class object for the raw type of the parameterized type. For example, if getrawtype were invoked on the parameterized type List it would return List.class. Even though a raw type is by definition a non-generic class or interface, getrawtype returns a Type instance rather than a Class, so a cast must be applied, as you saw in the typedesc program.
Generic Arrays
The last of the type related interfaces is genericarraytype. This represents array types in which the component type is a parameterized type or a type variable.[4] genericarraytype has a single method, getgenericcomponenttype, which returns the Type for the component type of the array, which will be either a parameterizedtype or a typevariable. For example, for a List[] field, getgenerictype would return a genericarraytype object whose getcomponenttype would return a parameterizedtype for List. [4] You may recall that you cannot create such an array, but you are permitted to declare variables of that type. The actual array creation can use only an unbounded wildcard type, such as new List[1]. When you first assign the new array to a more specific variable, such as a List[], you will get an “unchecked” warning because the compiler cannot guarantee that the current or future contents of the array will actually be List objects. Such arrays are inherently unsafe and should be used with extreme cautionyou should generally not create methods that return such arrays or take them as parameters. The component type object gets created when getgenericcomponenttype is invoked, so as you might expect, you may get a typenotpresentexception .
Last word
None of the interfaces described above define the tostring method, or any other general way to obtain a string representation of a typewith the exception of typevariable, which has the getname method. However, all the type objects will have a tostring method defined. With no specification of what tostring should return for Type objects, you cannot rely on tostring to give a reasonable representation of the type. If you want a string representation of a type, you will need to assemble it yourself from the information available. For example, if a wildcardtype object has no lower bound and an upper bound of X, then the wildcard is “?Extends X”. For a parameterizedtype you can construct the string representation using the raw type and the actual type parameter types. Exercise 16.9: Use reflection to write a program that will print a full declaration of a named class, including everything except the import statements, comments, and code for initializers, constructors, and methods. The member declarations should appear just as you would write them. You will need to use all the reflection classes you have seen. Also note that the tostring methods of many of the reflection objects will not provide the information you want in the correct format, so you will need to piece together the individual pieces of information.