-
Notifications
You must be signed in to change notification settings - Fork 0
3.2. Reflect
Low level reflection and introspection functionality for JavaBean properties. It supports bytecode generation, annotations and types. Largely inspired by java.lang.reflect and java.beans.Introspector.
Classes: 2 Status: Stable. Test coverage: 100%. Works on Android: Yes.
Required
- jdk 1.6+
Optional
- cglib If found in the classpath then the bytecode proxy generation will be automatically enabled to provide better performance.
-
Maven:
<dependencies> <dependency> <groupId>org.minimalcode</groupId> <artifactId>minimalcode-reflect</artifactId> <version>0.5.1</version><!-- or last version --> </dependency> <!-- Optional Cglib Dependency (avoid it on Android systems) --> <dependency> <groupId>cglib</groupId> <artifactId>cglib</artifactId> <version>3.1</version> </dependency> </dependencies>
Accessor methods are auto-discovered following the standard JavaBean patterns.
- Read Method: T getField() || boolean isField()
- Write Method: void setField(T field)
public class Book {
// Note: Getter, Isser and Setter methods are here hidden for readability
public boolean valid;// isValid() and setValid(boolean valid)
private String title;// getTitle() and setTitle(String title)
protected List<String> pages;// getPages() and setPages(List<String> pages)
}
-
For normal properties, accessor methods must be public and can be inherited from superclasses\interfaces.
-
For declared properties instead, accessor methods can have any valid modifier (public, protected, private, package-private...), but must be declared in the target class or interface, as any inheritance is ignored.
-
Only one accessor is required: properties can be read-only or write-only.
Bean<Book> bean = Bean.forClass(Book.class);// works with interfaces too
// Get Single Property
Property title = bean.getProperty("title");
Property pages = bean.getDeclaredProperty("pages");
Property iNull = bean.getDeclaredProperty("not-valid");// null, if not found
// All Properties
for(Property property : bean.getProperties()) {
System.out.println("Property: " + property.getName());
}
// All Declared Properties
for(Property property : bean.getDeclaredProperties()) {
System.out.println("Property: " + property.getName() + " (declared)");
}
Property::get and Property::set will invoke the getter\isser\setter methods. The bytecode proxy (if enabled) is automatically used to provide fast property access. The "raw" accessor methods are still available for additional flexibility. Furthermore, if a matching accessible field with the same name and type of the property is present, it will also be provided.
Object obj = new Book();
Bean<?> bean = Bean.forClass(obj.getClass());
Property title = bean.getProperty("title");
// if property exists
if (title != null) {
// Write
if (title.isWritable()) {
title.set(obj, "new-title");
}
// Read
if (title.isReadable()) {
String value = (String) title.get(obj);
System.out.println(title.getName() + " has value " + value + " in " + obj);
}
// Raw accessors and field
Method readMethod = title.getReadMethod();
Method writeMethod = title.getWriteMethod();
Field field = title.getField();
}
In addition to the plain type and the generic type (if any), a convenient actual type is provided. The actual type is the plain type for simple properties, the resolved element type for List, Iterable, Collection and array properties, or the Map value type for maps properties.
class Book {
// Note: Getter and Setter methods are here hidden for readability
private List<String> pages;// getPages() and setPages(List<String> pages)
}
Property pages = Bean.forClass(Book.class).getProperty("pages");
// if property exists
if (pages != null) {
Class<?> type = pages.getType();// List
Type genericType = pages.getGenericType();// ParameterizedType
Class<?> actualType = pages.getActualType();// Resolved element Type: String
if(List.class.isAssignableFrom(type)) {
System.out.println(pages.getName() + " is a " + type + " of " + actualType);// "pages is a ...List of ...String"
}
}
Annotations are statically collected from field, read method and write method accessors. Inheritance of annotations from superclasses is also supported.
class Book {
@MyAnnotationOne
private List<String> pages;
public List<String> getPages() { return pages; }
@MyAnnotationTwo
public void setPages(List<String> pages) { this.pages = pages; }
}
// Annotations
for(Property property : Bean.forClass(Book.class).getProperties()) {
if(property.isAnnotationPresent(MyAnnotationOne.class)) {
MyAnnotationOne annotation = property.getAnnotation(MyAnnotationOne.class);
System.out.println(property.getName() + " has @MyAnnotationOne with value " + annotation.value());
}
// All the AnnotatedElement methods are also supported
... = property.getAnnotations();
... = property.getAnnotation(MyAnnotationOne.class);
... = property.getAnnotationsByType(MyAnnotationOne.class);// only Jdk 1.8
... = property.getDeclaredAnnotations();
... = property.getDeclaredAnnotation(MyAnnotationTwo.class);
}
That's all... Less is more.