Reflective Check of a Non-Runtime Annotation
ID |
java.reflection_check_non_runtime_annot |
Severity |
high |
Remediation Complexity |
medium |
Remediation Risk |
low |
Remediation Effort |
medium |
Resource |
Code Smell |
Language |
Java |
Tags |
reliability |
Description
Reports a call to isAnnotationPresent(X.class) where the annotation type X does not carry RUNTIME retention. Only annotations declared @Retention(RetentionPolicy.RUNTIME) are kept in the loaded class and available to reflection; SOURCE and CLASS retained annotations (CLASS is also the default when @Retention is omitted) are discarded before the check can ever see them.
Rationale
A reflective presence check against a non-runtime annotation always evaluates to false, regardless of how the inspected element is actually annotated in source:
@Retention(RetentionPolicy.CLASS)
@interface LegacyCompatShim { }
void inspect(Method handler) {
if (handler.isAnnotationPresent(LegacyCompatShim.class)) { // always false
// this branch never runs, even when @LegacyCompatShim is present in source
}
}
The same happens with a handful of well-known JDK annotations that keep SOURCE retention, such as @Override, @SuppressWarnings and @SafeVarargs.
Remediation
Give the annotation RUNTIME retention so the JVM keeps it available to reflection:
@Retention(RetentionPolicy.RUNTIME)
@interface LegacyCompatShim { }
void inspect(Method handler) {
if (handler.isAnnotationPresent(LegacyCompatShim.class)) { // now reflects the real annotation state
// ...
}
}