The book Java Reflection in Action was published in 2004, but remains largely applicable eight years later. In this post, I review this book and cover its strengths and its weaknesses. In general, age is the common theme of most of its weaknesses, but I was surprised at how relevant its information still is. The book was written when JDK 1.4 was the commonly used release of Java and J2SE 5 was well on its way (released late September 2004). Given this relative timing, it's obvious that where this book suffers is in coverage of reflection-related features made available after the release of JDK 1.4.
Java reflection is a topic that most Java developers use on a daily basis, either directly or indirectly via popular frameworks and libraries that make use of reflection. However, it is probably true that many of us could use reflection to good effect in even more ways. For example, I have found reflection to be useful in writing Groovy scripts to manage Java application development.
Java Reflection in Action is written by the father/son team of Ira R. Forman and Nate Forman. It consists of ten chapters and is under 300 pages long. It's a quick read and one can understand the basics of Java reflection after reading only the first three chapters.
Chapter One ("A few basics"), Chapter Two ("Accessing fields reflectively"), and Chapter Three ("Dynamic loading and reflective construction") cover the basics of Java reflection. The contents of these three chapters are what most people probably expect in a book on Java reflection. Reading these three chapters alone provides the Java developer with the basics of Java reflection, including the advantages and some pitfalls of using reflection. These chapters provide detailed coverage of the most important reflection APIs and focus on constructing instances of Java classes and introspecting method and field information.
Early pages of Java Reflection in Action provide nice reflection-oriented definitions. For example, reflection is defined as "the ability of a running program to examine itself and its software environment, and to change what it does depending on what it finds" (p. 3). Introspection is defined as "reflective features that allow a program to examine itself" (p. 7) and metaobjects are defined as "objects that are part of a program's self-representation" (p. 9).
Chapter Four ("Using Java's dynamic proxy") is devoted to coverage of Java's dynamic proxies. This chapter states that java.lang.reflect.Proxy is "Java's only way of approximating method invocation intercession." The chapter defines intercession as "any reflective ability that modifies the behavior of the program by directly taking control of that behavior." This is one of the best treatments of dynamic proxies that I've seen.
Chapter Five ("Call stack introspection") covers use of StackTraceElement to provide introspection capabilities against the call stack (feature available since JDK 1.4). The authors point out that this is an approach for performing introspection on "execution state of the running application" (versus much of the commonly considered reflection that focuses on introspection of structure). The authors demonstrate accessing these stack trace elements via the Throwable.getStackTrace() call. Although this is still the approach for acquiring a stack's elements recommended in the Javadoc documentation for StackTraceElement, I prefer using Thread.getStackTrace() approach (see my blog post The Surprisingly Simple StackTraceElement), which was not made available until J2SE 5.
Chapter Six ("Using the class loader") provides nice coverage of Java class loaders. This is another subject that Java developers frequently run into directly and indirectly. The chapter provides a nice introduction to class loading in Java. Section 6.3 ("Multiple namespaces") provides information on a subject that Java developers definitely need to be aware of even when only using class loaders indirectly. This section explains that "a class object in the virtual machine is actually identified by both its fully qualified name and defining loader" (I added the emphasis).
Chapter Seven ("Reflective code generation") and Chapter Eight ("Design patterns") discuss using reflection capabilities to generate code. Specifically these chapters focus on using reflective capabilities to transform source classes into target classes. Specific design patterns applied to new classes that are implemented via transformation from existing source via reflective capabilities.
The Reflection API Trail of the Java Tutorials warns of "performance overhead" in its list of "reflection drawbacks." Chapter Nine ("Evaluating performance") of Java Reflection in Action addresses real and perceived performance issues related to reflection. Perhaps more importantly, the chapter provides ideas and issues related to the notoriously difficult application of micro-benchmarking. Besides covering micro-benchmarking itself, this chapter also introduces Amdahl's Law and reinforces use of proxies.
The first paragraph of the "Summary" of Chapter 10 made me think again about some of my preconceptions about Java reflection performance. This paragraph states (pp. 223-224):
Runtime performance is often cited as a disadvantage that renders reflection APIs impractical. Don't be taken in by this argument. Reflection is not slow! Over the first 50 years of software engineering, similar arguments were made against the move to high-level languages, against the move to virtual memory, against the move to object-oriented programming, and, most recently, against the move to garbage collection.
Chapter Ten ("Reflecting on the future") is, not surprisingly, the chapter made most obsolescent by the passage of time. Hindsight being 20/20, it is easy to see things missed in the Formans' predictions and speculation for the future of Java reflection. However, when one remembers that the book was published in 2004 before the general release of J2SE 5, this chapter's obsolescence is easily understood. The chapter specifically discusses the impact on reflection of several J2SE features we are now use regularly such as annotations, generics, and JSR 201 language extensions (Enums, for-each loop, static imports, and the cursed [my words] autoboxing). The chapter talks about other languages' likely advancements in reflection capabilities and provides a very brief introduction to aspect-oriented programming.
Java Reflection in Action packs a lot of information into its three appendices. Appendix A is the largest of the three and provides a description of "reflection and metaobject protocols and the relationship between the two." There is a two-page appendix devoted to basic UML (which is applied several times within the book) as well as appendix covering possible compilation problems of one of the book's basic code generation examples on certain operating systems versions.
Java Reflection in Action includes a lengthy list of authoritative references (nearly 100) for additional reading related to the topics covered in the book. It also provides a short glossary of key terms in reflection.
One of the book's most practical positive features is its supplying of a small set of static Java functions for making reflection tasks easier. Provided as part of the often referenced
Mopex class (in the mopex package), this code is available for download from the Manning site for the book. The
Mopex's class-level documentation describes the class: "This class provides a set of static methods that extend the Java metaobject protocol." The downloaded code samples include many more classes and code definitions used in the book as well.
Java Reflection in Action provides a thorough introduction to Java reflection. It features many positive aspects and its main negative aspect is its age (2004/JDK 1.4). Because versions of Java since JDK 1.4 have supplemented more than replaced reflective features, most of the book's content still applies today. Perhaps the most glaring omission from a book on Java reflection is Java 7's introduction of method handles, but few Java books in print today cover these.