Step 1 of 7 · Reading · ~3 min
Learn
Reflection and JVM
Reflection and Annotations
Until now the compiler knew every type your program touched. Reflection lets a program look at classes it was not written against: list their fields, read a private value, call a method by name. Annotations attach labels to those members, so code can read the labels and act on them. You will be able to write a small framework-style routine that walks an object and does something per labelled field.
The Class object
Every type has one Class<?> object describing it. You get it three ways, and from it you can enumerate members:
Class<?> a = String.class; // from a type name
Class<?> b = "hi".getClass(); // from an instance
Class<?> c = Class.forName("java.util.ArrayList"); // from a string; throws ClassNotFoundException
for (Field f : Order.class.getDeclaredFields()) {
System.out.println(f.getName() + " : " + f.getType().getSimpleName());
}
getDeclaredFields() returns every field declared in that class (private included, inherited ones excluded). getFields() returns only public fields, including inherited ones. The same split exists for methods and constructors. The order of the returned array is not specified; HotSpot usually gives declaration order, but code that depends on it breaks on another JVM, so sort by name when output order matters.
Reading, writing and calling
Field total = Order.class.getDeclaredField("total");
total.setAccessible(true); // switch off the private-access check
long t = (long) total.get(order); // get returns Object; primitives arrive boxed
total.set(order, 99L);
Method m = Order.class.getDeclaredMethod("discount", int.class);
m.setAccessible(true);
Object r = m.invoke(order, 10); // if discount throws, you get InvocationTargetException
Without setAccessible(true), touching a private member of another class throws IllegalAccessException. Since Java 9, members of JDK internal packages (for example java.base's private fields) refuse setAccessible with InaccessibleObjectException; your own classes on the classpath are fine. When a reflected method throws, invoke wraps the real exception in InvocationTargetException; read e.getCause().
Annotations: metadata you declare
An annotation type is declared with @interface. Two meta-annotations decide whether anyone can read it:
@Retention(RetentionPolicy.RUNTIME) // kept in the .class file AND visible to reflection
@Target(ElementType.FIELD) // may only be placed on fields
@interface Range {
int min() default 0;
int max();
}
class Settings {
@Range(min = 1, max = 65535) int port = 70000;
@Range(max = 10) int retries = 3;
String name = "svc"; // no annotation
}
The default retention is CLASS: the annotation is in the bytecode but reflection returns null for it. Forgetting RUNTIME is the classic reason a framework "ignores" your annotation. To use it, ask each member:
for (Field field : Settings.class.getDeclaredFields()) {
Range r = field.getAnnotation(Range.class); // null when absent
if (r == null) continue;
field.setAccessible(true);
int v = field.getInt(settings);
if (v < r.min() || v > r.max()) System.out.println(field.getName() + " out of range");
}
Class-level annotations work the same: Settings.class.getAnnotation(...).
Cost
Reflective access is slower than a direct call, defeats compile-time type checks and breaks encapsulation. If the labels are known at build time, an annotation processor or generated code avoids all three.
Common mistakes
- Annotation without
@Retention(RUNTIME).getAnnotationreturnsnulland the labelled field is silently skipped. - Assuming
getDeclaredFields()is in source order. The output works on your machine and reorders elsewhere. - Skipping the null check on
getAnnotation. Unlabelled fields cause aNullPointerException. - Printing the
FieldorMethodobject. You getprivate int Settings.port, not the value.
Your exercise
Write a routine that turns any labelled object into a SQL INSERT statement by reading its class-level and field-level annotations. The annotations and the entity class are declared in the starter. The most likely mistake is emitting columns in whatever order reflection returns instead of the order the output requires.
Discussion
Ask a question, share an insight, or help someone who’s stuck.
Sign in to post a comment or reply.
Loading…