Reflection on Tigerby Michael Nascimento Santos
03/08/2004
from http://today.java.net/lpt/a/46
Much has been written about the new language features in J2SE 1.5, Tiger. Many, such as the introduction of enums, generics, and metadata, involve changing bytecode. These additions to the language require modifications to existing APIs. One of the APIs most radically affected by these changes is the Reflection API. Reflection has existed since JDK 1.1, and many frameworks, libraries, and utilities have been built upon it. This article examines the modifications to the Reflection API that are now available to the public as part of JDK 1.5 beta 1, and shows how you can take advantage of them in your code.
New Kids on the Hierarchy
A few interfaces and exceptions have been added to java.lang.reflect, as well as to java.lang. Some "old classes" -- such as Class, Method, Constructor, and others -- have been retrofitted to implement them. In this section we will look at these added interfaces.
Reflections on Metadata
The AnnotatedElement interface is implemented by any class that represents an element that may contain annotations (also known as metadata), as specified by JSR-175. The Class, Constructor, Field, Method, and Package each have been modified to implement AnnotatedElement. The interface specifies methods to retrieve all annotations, annotations by type, annotations declared only in a particular class (i.e., not including superclasses), and also a method to query whether or not a class contains an annotation of a specific type. The following code snippets demonstrate the use of these methods. Note that you must have some knowledge about how annotations work to fully understand this example.
First, create a custom annotation that we will later examine with reflection.
Next, create a class AnnotatedElementTest and mark it and an instance method aMethod() as being annotated with the SampleAnnotation class you just created:
You can detect and get a handle to the Class annotations by creating a Class object and invoking the methods is AnnotationPresent() and getAnnotations(). You can repeat the process for the annotated method. The getAnnotations() method is used to retrieve all of the annotations this method has that are retained until runtime. When you compile and run AnnotatedElementTest, you will notice that @Deprecated is not shown. That is because it's only part of the source code and it's not present, even in the class file. Here's the listing for AnnotatedElementTest.
The following additions to the Reflection APIs allow you to obtain information about generics.
Here is an example of how to use some of these classes and interfaces related to generics.
One thing most people erroneously assume is that they will be able to extract information about the actual binding of a type variable once they have a variable of a parameterized type. For example, if there is a method that accepts as a parameter that is declared as Collection, inside of the method it is impossible to know what type actually is being used for E. That happens because generics are implemented in Java using erasure, which means that the actual type information is lost. There is no way to find what type was actually used to represent a type variable, even if you think that intentionally adding a method with this information would be possible. An example is shown below:
This code doesn't compile, since information about E is defined in runtime and erasured, while a reference to .class is statically resolved. That said, let's consider which changes have been made to the "old classes" that have been part of the API before Tiger:
[list]
Member: This interface, which is implemented by Class, Field and Method, now defines a new method, isSynthetic(). According to its Javadoc, it "Returns true if this member was introduced by the compiler and returns false otherwise".
Proxy: getProxyClass() had its signature changed to accept a varargs Class parameter for interfaces instead of the original array one.
Class: java.lang.Class now looks scarier than ever if you don't have a clue about generics. Even its declaration has changed to include a type variable, T, for the actual class it represents. This allows some interesting reflexive constructions and methods to appear, such as the new cast(Object) method. This method takes an object and returns an instance of T. In code, it works like this:
Several methods that accepted arrays of Class have been retrofitted to support varargs, such as getConstructor, getDeclaredConstructor, getDeclaredMethod, and getMethod. This makes it easier to call these methods, as shown below:
The getEnumConstants() method returns a generic array type -- T[] -- with all of the declared enum instances, given that this instance represents an Enum (i.e., returns true for the also new isEnum() method).
The getGenericInterfaces() and getGenericSuperclass() methods are useful if you implement or extend a parameterized type, such as List. These methods return arrays of Type and Type, respectively, allowing access to the generic declaration of a superclass or superinterface. However, as the example in GenericsTest shows, dealing with different subclasses of Type requires some ugly code, since it is only a marker interface (i.e., it does not define any methods).
A minor change has been made to getSuperclass(). Now it returns Class<? super T> instead of the raw type. Most developers will not find it particularly useful, but it is an example of better type safety added by generics.
The newInstance() method now returns a type variable, T. This eliminates the need for a cast in some common uses, such as in:
Field: Trivial changes have been made to this class. A new getter method has been added -- isEnumConstant() -- as well as getGenericType(), which is useful if the field's type is a type variable or a parameterized type.
Constructor and Method: Constructor's changes are a subset of the ones made to Method, so they are grouped for brevity. getGenericExceptionTypes() and getGenericParameterTypes() return arrays of Type, allowing access to parameterized types that might be part of the method/constructor signature.
The getParameterAnnotations() method return a bi-dimensional array of Annotations present in each parameter type. The first dimension represents each parameter, and the second one its annotations.
The isVarArgs() method indicates if this constructor/method takes a varargs parameter. These are the methods both have in common.
One method in Constructor, newInstance, has been changed to accept varargs as its parameter type. Again, this change is intended to make easier to call this method.
Changes exclusive to Method include the addition of getGenericReturnType(), for return types involving generics, and a isBridge() method, which has this puzzling Javadoc description: "Returns true if and only if this method is a bridge method as defined by the Java Language Specification." A new version of invoke that takes its args parameter as varargs instead of an array of Object is also provided in order to make the API simpler.
[/list]
Conclusion
Significant changes have been made to Java in order to make it more powerful and expressive. However, the side effect is the increased difficulty in using some Java APIs, especially Reflection, which must provide support for nearly all enhancements that require changes to bytecode. Luckily, as developers use the new API and have more resources available about the changes, such as this article, there will be more pros than cons.
Michael Nascimento Santos is a seasoned developer with more than 8 years of experience with the Java platform, from J2ME to J2EE, and over 14 years of pratical programming experience.
03/08/2004
from http://today.java.net/lpt/a/46
Much has been written about the new language features in J2SE 1.5, Tiger. Many, such as the introduction of enums, generics, and metadata, involve changing bytecode. These additions to the language require modifications to existing APIs. One of the APIs most radically affected by these changes is the Reflection API. Reflection has existed since JDK 1.1, and many frameworks, libraries, and utilities have been built upon it. This article examines the modifications to the Reflection API that are now available to the public as part of JDK 1.5 beta 1, and shows how you can take advantage of them in your code.
New Kids on the Hierarchy
A few interfaces and exceptions have been added to java.lang.reflect, as well as to java.lang. Some "old classes" -- such as Class, Method, Constructor, and others -- have been retrofitted to implement them. In this section we will look at these added interfaces.
Reflections on Metadata
The AnnotatedElement interface is implemented by any class that represents an element that may contain annotations (also known as metadata), as specified by JSR-175. The Class, Constructor, Field, Method, and Package each have been modified to implement AnnotatedElement. The interface specifies methods to retrieve all annotations, annotations by type, annotations declared only in a particular class (i.e., not including superclasses), and also a method to query whether or not a class contains an annotation of a specific type. The following code snippets demonstrate the use of these methods. Note that you must have some knowledge about how annotations work to fully understand this example.
First, create a custom annotation that we will later examine with reflection.
package net.java.reflection; import java.lang.annotation.Retention; import static java.lang.annotation.RetentionPolicy.*; @Retention(RUNTIME) public @interface SampleAnnotation { public String value(); }
Next, create a class AnnotatedElementTest and mark it and an instance method aMethod() as being annotated with the SampleAnnotation class you just created:
@SampleAnnotation("AnnotatedElementTest") public class AnnotatedElementTest { @SampleAnnotation("aMethod") @Deprecated public void aMethod() { } //... }
You can detect and get a handle to the Class annotations by creating a Class object and invoking the methods is AnnotationPresent() and getAnnotations(). You can repeat the process for the annotated method. The getAnnotations() method is used to retrieve all of the annotations this method has that are retained until runtime. When you compile and run AnnotatedElementTest, you will notice that @Deprecated is not shown. That is because it's only part of the source code and it's not present, even in the class file. Here's the listing for AnnotatedElementTest.
package net.java.reflection; import java.lang.annotation.Annotation; import java.lang.reflect.Method; @SampleAnnotation("AnnotatedElementTest") public class AnnotatedElementTest { @SampleAnnotation("aMethod") @Deprecated public void aMethod() { } public static void main(String[] args) throws Exception { Class clazz = AnnotatedElementTest.class; System.out.println(clazz.isAnnotationPresent(SampleAnnotation.class)); System.out.println(clazz.getAnnotation(SampleAnnotation.class)); Method method = clazz.getMethod("aMethod"); for (Annotation a : method.getAnnotations()) { System.out.println(a); } } }Working with Generics
The following additions to the Reflection APIs allow you to obtain information about generics.
Type: This new marker interface was introduced in order to represent all of the types that can be used in Java. These include raw types; i.e., Classes, parameterized types, array types, type variables, and primitive types. java.lang.Class, for example, has been retrofitted to implement this new interface.
TypeVariable: This interface represents type variables of all kinds. For example, in Class, T is a type variable. There are methods for obtaining the name of the variable, its bounds (superclasses) and its GenericDeclaration -- the last one is explained below.
GenericDeclaration: This interface, implemented by Class, Constructor and Method, indicates they might declare TypeVariables. It defines a sole method, getTypeParameters(), returning an array containing all of the TypeVariables in the order in which they were originally declared.
ParameterizedType: Represents a parameter type with a generic type, such as Class. It has methods that allow retrieval of the actual type arguments (T, in the previous example), to get the enclosing class of this type, if any, and to retrieve the raw type declared (Class, in this case).
GenericArrayType: Represents an array of a generic type, such as T[]. It has a method that allows one to retrieve the component type of the array (in this case, T).
WildcardType: Represents a type that uses wildcards, such as ? or ? extends T. The methods provided return the lower bounds of the type (in T super Integer, Integer would be a lower bound) and the upper bounds (for ?, Object is the upper bound).
MalformedParameterizedTypeException: This exception is thrown if the parameterized type cannot be instantiated at runtime for some reason. It is a RuntimeException.
GenericSignatureFormatError: This exception indicates a low-level bytecode error and it should be rare. However, using JDK 1.5 beta 1, is possible to get this error when you try to call getTypeParameters() in an instance of a system class that defines a non-trivial type variable, such as >. An example of such a class instance is TypeVariable.class.
TypeNotPresentException: This exception can occur when some code tries to access a type using its name, but it cannot be found. It is the only new exception related to reflection that is not defined in the expected package, but in java.lang.
Here is an example of how to use some of these classes and interfaces related to generics.
package net.java.reflection; import java.lang.reflect.GenericArrayType; import java.lang.reflect.Method; import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; import java.lang.reflect.TypeVariable; import java.lang.reflect.WildcardType; import java.util.Map; // This class declaration defines a type variable T public class GenericsTest { // This method is used in the reflection example below public void aMethod(Class<? extends T> clazz1, Class clazz2, T[] ts) { } // Prints information about a type variable private static void print(TypeVariable v) { System.out.println("Type variable"); System.out.println("Name: " + v.getName()); System.out.println("Declaration: " + v.getGenericDeclaration()); System.out.println("Bounds:"); for (Type t : v.getBounds()) { print(t); } } // Prints information about a wildcard type private static void print(WildcardType wt) { System.out.println("Wildcard type"); System.out.println("Lower bounds:"); for (Type b : wt.getLowerBounds()) { print(b); } System.out.println("Upper bounds:"); for (Type b : wt.getUpperBounds()) { print(b); } } // Prints information about a parameterized type private static void print(ParameterizedType pt) { System.out.println("Parameterized type"); System.out.println("Owner: " + pt.getOwnerType()); System.out.println("Raw type: " + pt.getRawType()); for (Type actualType : pt.getActualTypeArguments()) { print(actualType); } } // Prints information about a generic array type private static void print(GenericArrayType gat) { System.out.println("Generic array type"); System.out.println("Type of array: "); print(gat.getGenericComponentType()); } /** * Prints information about a type. The nested * if/else-if chain calls the * appropriate overloaded print method for the * type. If t is just a Class, * we print it directly. */ private static void print(Type t) { if (t instanceof TypeVariable) { print((TypeVariable)t); } else if (t instanceof WildcardType) { print((WildcardType)t); } else if (t instanceof ParameterizedType) { print((ParameterizedType)t); } else if (t instanceof GenericArrayType) { print((GenericArrayType)t); } else { System.out.println(t); } } public static void main(String[] args) throws Exception { // Some classes we are going to play with Class[] classes = new Class[] {Class.class, Map.class, GenericsTest.class}; // Iterate the array for each class instance... for (Class clazz : classes) { // Prints its name and ... System.out.println("Class: " + clazz); // Iterate for each type variable defined by this class for (TypeVariable v : clazz.getTypeParameters()) { print(v); } System.out.println(); } System.out.println("Reflective information " + "about the parameters of aMethod"); // Iterate for each method... for (Method method : GenericsTest.class.getDeclaredMethods()) { // Until we find aMethod if (method.getName().equals("aMethod")) { // Then, go over all parameters ... for (Type t : method.getGenericParameterTypes()) { System.out.println("Parameter:"); // And print reflexive information about them print(t); System.out.println(); } break; } } } }Changes to the "Old" API
One thing most people erroneously assume is that they will be able to extract information about the actual binding of a type variable once they have a variable of a parameterized type. For example, if there is a method that accepts as a parameter that is declared as Collection, inside of the method it is impossible to know what type actually is being used for E. That happens because generics are implemented in Java using erasure, which means that the actual type information is lost. There is no way to find what type was actually used to represent a type variable, even if you think that intentionally adding a method with this information would be possible. An example is shown below:
public class GenericClass { // some code public Class getElementType() { return E.class; } //more code }
This code doesn't compile, since information about E is defined in runtime and erasured, while a reference to .class is statically resolved. That said, let's consider which changes have been made to the "old classes" that have been part of the API before Tiger:
[list]
Member: This interface, which is implemented by Class, Field and Method, now defines a new method, isSynthetic(). According to its Javadoc, it "Returns true if this member was introduced by the compiler and returns false otherwise".
Proxy: getProxyClass() had its signature changed to accept a varargs Class parameter for interfaces instead of the original array one.
Class: java.lang.Class now looks scarier than ever if you don't have a clue about generics. Even its declaration has changed to include a type variable, T, for the actual class it represents. This allows some interesting reflexive constructions and methods to appear, such as the new cast(Object) method. This method takes an object and returns an instance of T. In code, it works like this:
Class cs = Serializable.class; // more code ... Object o = ...; Serializable s = cs.cast(o);
Several methods that accepted arrays of Class have been retrofitted to support varargs, such as getConstructor, getDeclaredConstructor, getDeclaredMethod, and getMethod. This makes it easier to call these methods, as shown below:
// This now works for no-args methods: // It is equivalent to getMethod("aMethod", new Class[] {}); Method m1 = clazz.getMethod("aMethod"); // And this for methods that takes many arguments. // It is equivalent to //getMethod("aMethod", new Class[] {String.class, Object.class}); Method m2 = clazz.getMethod("aMethod", String.class, Object.class);Also, getEnclosingConstructor() and getEnclosingMethod() are useful for retrieving the instance of the constructor or method where a local class or an anonymous inner class have been defined inside. They only return meaningful values if called in Class instances representing these two types of classes; otherwise, they return null.
The getEnumConstants() method returns a generic array type -- T[] -- with all of the declared enum instances, given that this instance represents an Enum (i.e., returns true for the also new isEnum() method).
The getGenericInterfaces() and getGenericSuperclass() methods are useful if you implement or extend a parameterized type, such as List. These methods return arrays of Type and Type, respectively, allowing access to the generic declaration of a superclass or superinterface. However, as the example in GenericsTest shows, dealing with different subclasses of Type requires some ugly code, since it is only a marker interface (i.e., it does not define any methods).
A minor change has been made to getSuperclass(). Now it returns Class<? super T> instead of the raw type. Most developers will not find it particularly useful, but it is an example of better type safety added by generics.
The newInstance() method now returns a type variable, T. This eliminates the need for a cast in some common uses, such as in:
// Supposing E represents an interface ... // If we get an instance of an implementation class such as: Class clazz; // We now can simply do: E e = clazz.newInstance();
Field: Trivial changes have been made to this class. A new getter method has been added -- isEnumConstant() -- as well as getGenericType(), which is useful if the field's type is a type variable or a parameterized type.
Constructor and Method: Constructor's changes are a subset of the ones made to Method, so they are grouped for brevity. getGenericExceptionTypes() and getGenericParameterTypes() return arrays of Type, allowing access to parameterized types that might be part of the method/constructor signature.
The getParameterAnnotations() method return a bi-dimensional array of Annotations present in each parameter type. The first dimension represents each parameter, and the second one its annotations.
The isVarArgs() method indicates if this constructor/method takes a varargs parameter. These are the methods both have in common.
One method in Constructor, newInstance, has been changed to accept varargs as its parameter type. Again, this change is intended to make easier to call this method.
Changes exclusive to Method include the addition of getGenericReturnType(), for return types involving generics, and a isBridge() method, which has this puzzling Javadoc description: "Returns true if and only if this method is a bridge method as defined by the Java Language Specification." A new version of invoke that takes its args parameter as varargs instead of an array of Object is also provided in order to make the API simpler.
[/list]
Conclusion
Significant changes have been made to Java in order to make it more powerful and expressive. However, the side effect is the increased difficulty in using some Java APIs, especially Reflection, which must provide support for nearly all enhancements that require changes to bytecode. Luckily, as developers use the new API and have more resources available about the changes, such as this article, there will be more pros than cons.
Michael Nascimento Santos is a seasoned developer with more than 8 years of experience with the Java platform, from J2ME to J2EE, and over 14 years of pratical programming experience.
相关推荐
on reflection. Even occasional users will immediately adopt the book’s patterns and idioms to solve common problems. Many of the examples can be directly adapted for customized solutions in diverse ...
在JavaScript编程中,"reflection"通常指的是对象反射机制,它允许程序在运行时检查自身的行为和属性。在本例中,我们关注的是JavaScript中的倒影效果,这可能是指在图形或者用户界面中创建的一种视觉效果,使元素看...
Unity Planar Reflection平面反射
《利用反射变化自动消除图像反射》是一篇关于图像处理技术的深度研究,主要探讨了如何通过分析和利用图像中的反射变化来实现自动化地去除图像中的反射现象。在图像处理领域,尤其是在摄影、数字媒体和视觉识别中,...
C#反射(Reflection)详解 什么是反射 命名空间和装配体的关系
reflection的ppt介绍,当中包括大学课程reflection的详细讲解
Simulating planar reflection using two-pass rendering and texture mapping 1. There ‘s a table with flat rectangular semi-reflective table-top in the 3D scene 2. On/Around the table are some 3D ...
B4A Reflection Library
on reflection. Even occasional users will immediately adopt the book’s patterns and idioms to solve common problems. Many of the examples can be directly adapted for customized solutions in diverse ...
在Java编程语言中,Reflection(反射)是一种强大的工具,它允许程序在运行时检查和操作类、接口、字段和方法的信息。Reflection的概念基于这样一个事实:Java程序不仅可以执行预先定义的操作,还可以在运行时动态地...
在《Java Reflection in Action》这本书中,作者深入探讨了这一主题,帮助开发者理解并有效地利用Java反射机制。这本书是2005年出版的英文版,对于想要提升Java编程技能,特别是对动态类型和元编程感兴趣的开发者来...
gaussian_reflection
Managed.Reflection, System.Reflection [.Emit ]的托管替换 Managed.ReflectionManaged.Reflection 是对 System.Reflection 和 System.Reflection.Emit的完全管理的替换。 System.Reflection 不同,它不绑定到
### 反射(Reflection)详解 #### 一、什么是反射? 反射是.NET框架中的一个强大功能,它允许程序在运行时获取自身的信息,并且能够动态地调用方法或创建对象。这种能力对于实现某些高级特性非常有用,比如依赖...
在Laravel框架中,Reflection是一个重要的概念,它主要涉及到PHP的反射API(Reflection API)。反射是一种在运行时分析类、接口、方法等元数据的技术,它允许程序在执行过程中检查自身的行为和结构。在Laravel中,...
从标题《信息安全_数据安全_Reflection_on_trusting_trust.pdf》可以看出,本文将探讨数据安全领域中的信任问题,以及相关的云安全、漏洞挖掘、自动化、安全威胁和安全对抗等方面的知识。 在云安全方面,随着越来越...
在"Reflection.zip"压缩包中,包含的"Reflection"文件很可能是一个示例或者教程,详细讲解了如何使用Java反射API进行各种操作。现在,我们将深入探讨Java反射机制及其主要应用。 1. **什么是Java反射**: Java反射...