[5] For compilers that support "unchecked" as a warning type, this situation is an ideal
candidate for use of the @SuppressWarnings annotation that was mentioned on page 396,
Exercise 16.10: Modify Interpret further to allow users to specify a type and size of array to create; set
and get the elements of that array; and access fields and invoke methods on specific elements of the array.
16.11. Packages
If you invoke the getPackage method on a Class object, you get a Package object that describes the
package in which the class lives (the Package class is defined in java.lang). You can also get a
Package object by invoking the static method getPackage with the name of the package, or you can use
the static getPackages method which returns an array of all known packages in the system. The getName
method returns the full name of the package.
Package objects are used differently than the other reflective typesyou can't create or manipulate packages at
run time. You use Package objects to obtain information about a package, such as its purpose, who created
it, what version it is, and so on. We defer a discussion on this until we look at packages in detail in Chapter
18.
16.12. The Proxy Class
The Proxy class lets you create classes at runtime that implement one or more interfaces. This is an
advanced, rarely needed feature, but when needed it is quite useful.
Suppose, for example, you want to log calls to an object so that when a failure happens you can print the last
several methods invoked on the object. You could write such code by hand for a particular class, with a way
to turn it on for a particular object. But that requires custom code for each type of object you want to monitor,
as well as each object checking on each method invocation to see if calls should be logged.
You could instead write a general utility that used a Proxy-created class to log a call history. Objects created
by that class would implement the relevant interfaces, interposing code that you provide between the caller's
invocation of a method and the object's execution of it.
The Proxy model is that you invoke Proxy.getProxyClass with a class loader and an array of
interfaces to get a Class object for the proxy. Proxy objects have one constructor, to which you pass an
InvocationHandler object. You can get a Constructor object for this constructor from the Class
object and use newInstance (passing in an invocation handler) to create a proxy object. The created object
implements all the interfaces that were passed to getProxyClass, as well as the methods of Object. As a
shortcut to get a proxy object, you can invoke Proxy.newProxyInstance, which takes a class loader, an
array of interfaces, and an invocation handler. When methods are invoked on the proxy object, these method
invocations are turned into calls to the invocation handler's invoke method.
Given all that, here is how a general debug logging class might look:
public class DebugProxy implements InvocationHandler {
private final Object obj; // underlying object
private final List
private final List
private DebugProxy(Object obj) {
this.obj = obj;