Class ReflectionUtil
Replaces hutool's ReflectUtil / AnnotationUtil.
- Since:
- 6.2.0
- Author:
- wisdomme
-
Constructor Summary
Constructors -
Method Summary
Modifier and TypeMethodDescriptionfindSlotOverriding(List<List<Method>> bucket, Method candidate) Finds the slot inbucketwhose declarations overridecandidate, ornullif none does.getAllFields(Class<?> clazz) Gets all fields of a class (including superclasses).getAllMethods(Class<?> clazz) Collects every method visible on the given class, walking the hierarchy and keeping only the most specific declaration of each overridable method.static <A extends Annotation>
AgetAnnotation(Class<?> clazz, Class<A> annotationClass) Gets an annotation on a class.static <A extends Annotation>
AgetAnnotation(Field field, Class<A> annotationClass) Gets an annotation on a field.static FieldGets the specified field.static Field[]Gets all fields of a class (including superclasses).static ObjectgetFieldValue(Object obj, Field field) Gets a field's value.static ObjectgetFieldValue(Object obj, String fieldName) Gets a field's value.static Method[]getMethods(Class<?> clazz) Gets all methods of a class (including superclasses).static Method[]getMethods(Class<?> clazz, Predicate<Method> filter) Gets the methods that satisfy a condition.private static Class<?> getPrimitiveWrapper(Class<?> primitive) Gets the wrapper class corresponding to a primitive type.static booleanhasAnnotation(Class<?> clazz, Class<? extends Annotation> annotationClass) Whether a class carries the specified annotation.static booleanhasAnnotation(Field field, Class<? extends Annotation> annotationClass) Whether a field carries the specified annotation.static <T> TInvokes a method.private static booleanisAssignable(Class<?> target, Class<?> source) Whether the type is assignable.static booleanisPackagePrivate(int modifiers) True for default (package-private) access: neitherpublic,protected, norprivate.static <T> TnewInstance(Class<T> clazz) Creates an instance (using the no-arg constructor).static <T> TnewInstance(Class<T> clazz, Object... params) Creates an instance (using a parameterized constructor).static <T> TnewInstanceIfPossible(Class<T> clazz) Creates an instance (using the no-arg constructor, private constructors included).static booleanWhethersuboverridessupper JLS 8.4.8.1.static StringpackageNameOf(Class<?> clazz) The class's package name, or the empty string for the unnamed package.static <A extends Annotation>
AresolveMethodOrClassAnnotation(Method method, Class<?> executorClass, Class<A> annotationType) WR-02 (05-REVIEW.md): resolvesannotationTypein THREE steps, most-derived-first: (1)method's own declaration, (2)executorClass's own class-level declaration -- the CONCRETE, most-derived executor class actually loaded, the SAME classPluginManager's load-time gate inspects viaexecutor.getClass()-- (3)method.getDeclaringClass()'s class-level declaration, for a shared abstract base that declares BOTH the mapping method and the annotation together.static <A extends Annotation>
AresolveMethodOrClassAnnotation(Method method, Class<A> annotationType) ResolvesannotationTypeformethod, preferring a declaration on the method itself and falling back to one on the method's declaring class -- most-derived wins, the same precedenceSenderTypeValidatoralready applies for@CmdTargetinside this same validator chain, and the precedent Spring's@Transactionaland Spring Security's@PreAuthorizeboth document for a class-vs-method annotation conflict.static voidsetFieldValue(Object obj, Field field, Object value) Sets a field's value.static voidsetFieldValue(Object obj, String fieldName, Object value) Sets a field's value.
-
Constructor Details
-
ReflectionUtil
private ReflectionUtil()
-
-
Method Details
-
getAllFields
Gets all fields of a class (including superclasses).- Parameters:
clazz- the class- Returns:
- the field list
-
getFields
Gets all fields of a class (including superclasses).- Parameters:
clazz- the class- Returns:
- the field array
-
getFieldValue
Gets a field's value.- Parameters:
obj- the objectfield- the field- Returns:
- the field value
-
getFieldValue
Gets a field's value.- Parameters:
obj- the objectfieldName- the field name- Returns:
- the field value
-
setFieldValue
Sets a field's value.- Parameters:
obj- the objectfield- the fieldvalue- the value
-
setFieldValue
Sets a field's value.- Parameters:
obj- the objectfieldName- the field namevalue- the value
-
getField
Gets the specified field.- Parameters:
clazz- the classfieldName- the field name- Returns:
- the field, or
nullif not found
-
newInstance
Creates an instance (using the no-arg constructor).- Type Parameters:
T- the type- Parameters:
clazz- the class- Returns:
- the instance
-
newInstance
Creates an instance (using a parameterized constructor).- Type Parameters:
T- the type- Parameters:
clazz- the classparams- the constructor parameters- Returns:
- the instance
-
isAssignable
Whether the type is assignable. -
getPrimitiveWrapper
Gets the wrapper class corresponding to a primitive type. -
newInstanceIfPossible
Creates an instance (using the no-arg constructor, private constructors included).- Type Parameters:
T- the type- Parameters:
clazz- the class- Returns:
- the instance
-
getAnnotation
Gets an annotation on a class.- Type Parameters:
A- the annotation type- Parameters:
clazz- the classannotationClass- the annotation type- Returns:
- the annotation instance, or
nullif absent
-
getAnnotation
Gets an annotation on a field.- Type Parameters:
A- the annotation type- Parameters:
field- the fieldannotationClass- the annotation type- Returns:
- the annotation instance, or
nullif absent
-
hasAnnotation
Whether a class carries the specified annotation.- Parameters:
clazz- the classannotationClass- the annotation type- Returns:
- whether the annotation is present
-
hasAnnotation
Whether a field carries the specified annotation.- Parameters:
field- the fieldannotationClass- the annotation type- Returns:
- whether the annotation is present
-
resolveMethodOrClassAnnotation
public static <A extends Annotation> A resolveMethodOrClassAnnotation(Method method, Class<A> annotationType) ResolvesannotationTypeformethod, preferring a declaration on the method itself and falling back to one on the method's declaring class -- most-derived wins, the same precedenceSenderTypeValidatoralready applies for@CmdTargetinside this same validator chain, and the precedent Spring's@Transactionaland Spring Security's@PreAuthorizeboth document for a class-vs-method annotation conflict.Only the method's OWN declaring class is consulted (via
method.getDeclaringClass().getAnnotation(...)) -- not the class's own ancestors, since neither@CmdCDnor@UsageLimitis@Inherited. This matchesgetAllMethods(Class)'s own hierarchy walk: a method inherited from a superclass without being overridden is returned with that superclass already as itsgetDeclaringClass(), so a class-level annotation on that superclass is still found without any extra ancestor walk here.Convenience delegate to
resolveMethodOrClassAnnotation(Method, Class, Class)withexecutorClassasnull-- kept for callers (and existing tests) that only ever had aMethodto resolve against, not the dispatching executor's concrete class. Prefer the 3-argument overload when the concrete executor class is known: it also checks that class's own declaration, closing WR-02 (05-REVIEW.md) -- a class-level annotation declared on a concrete executor SUBCLASS, inherited by an unoverridden@CmdMappingmethod whosegetDeclaringClass()is an ancestor, is invisible to this 2-argument form.- Type Parameters:
A- the annotation type- Parameters:
method- the matched command mapping methodannotationType- the annotation type to resolve- Returns:
- the method-level annotation if present, otherwise the declaring class's
annotation, or
nullif neither declares it - Since:
- 6.3.0
-
resolveMethodOrClassAnnotation
public static <A extends Annotation> A resolveMethodOrClassAnnotation(Method method, @Nullable Class<?> executorClass, Class<A> annotationType) WR-02 (05-REVIEW.md): resolvesannotationTypein THREE steps, most-derived-first: (1)method's own declaration, (2)executorClass's own class-level declaration -- the CONCRETE, most-derived executor class actually loaded, the SAME classPluginManager's load-time gate inspects viaexecutor.getClass()-- (3)method.getDeclaringClass()'s class-level declaration, for a shared abstract base that declares BOTH the mapping method and the annotation together.Step (2) is what
resolveMethodOrClassAnnotation(Method, Class)(the 2-arg overload) cannot do: for an inherited, unoverridden@CmdMappingmethod,method.getDeclaringClass()is whatever ANCESTOR first declared it -- never the concrete subclass, since neither@CmdCDnor@UsageLimitis@Inherited. A class-level annotation declared ONLY on such a subclass previously passedPluginManager's load-time gate (which correctly checksexecutor.getClass()) but was never found byCooldownValidator/UsageLockValidatorat runtime -- the gate's "fine to load" was a false assurance. Step (3) is kept as a further fallback so a shared abstract base that declares both the mapping and the annotation together -- the pre-WR-02 working case -- is unaffected.executorClassis checked as a DIRECT declaration only (no ancestor walk of its own): if the concrete class itself does not carry the annotation, step (3) already covers the "declared on an ancestor of the mapping method" case, and there is no third distinct class to consult for the same annotation type.- Type Parameters:
A- the annotation type- Parameters:
method- the matched command mapping methodexecutorClass- the concreteBaseCommandExecutorclass dispatching this command -- the SAME classPluginManager's load-time gate inspects -- ornullto fall back to the pre-WR-02, declaring-class-only resolutionannotationType- the annotation type to resolve- Returns:
- the resolved annotation, or
nullif none of method,executorClass, or the method's declaring class carries one - Since:
- 6.3.0
-
invoke
Invokes a method.- Type Parameters:
T- the return type- Parameters:
obj- the objectmethod- the methodargs- the arguments- Returns:
- the method's return value
-
getMethods
Gets the methods that satisfy a condition.Delegates to
getAllMethods(Class)so callers get the same de-duplicated, bridge/synthetic-free view of the hierarchy - a rawgetDeclaredMethods()walk here would double-count an overridden method (or, once a bean is AOP-proxied, could return both the proxy's intercepted override and the original method as separate hits for the same logical method).- Parameters:
clazz- the classfilter- the filter condition- Returns:
- the method array
-
getMethods
Gets all methods of a class (including superclasses).- Parameters:
clazz- the class- Returns:
- the method array
-
getAllMethods
Collects every method visible on the given class, walking the hierarchy and keeping only the most specific declaration of each overridable method.Class.getDeclaredMethods()returns only the methods a class declares itself, andClass.getMethods()returns only public ones. Neither is right for annotation scanning on a bean that may be an AOP proxy: the proxy declares overrides only for intercepted methods, so scanning it directly loses every annotation on the rest (issue #190). Walking the hierarchy recovers them, and collapsing overrides keeps a callback from firing once per level when an override repeats its parent's annotation.Declarations are grouped into slots using
overrides(Method, Method), and each slot contributes exactly one entry: the most derived declaration, which is the one whose annotations a scanner should see. A slot remembers every declaration folded into it, not just that representative, because overriding is transitive (JLS 8.4.8.1): inx.A.m()(package-private) →x.B.m()(public) →y.C.m()(public), the leaf does not directly override the root - different packages - but does so through the middle declaration, and all three are one method. Testing a candidate only against the surviving representative would keep the root as a spurious second entry.privateandstaticmethods never participate in overriding, so a same-named, same-parameter declaration at another level always starts its own slot and both survive.Bridge and synthetic methods are skipped: they carry no author-written annotations and, being compiler artifacts, are never the method a scanner means to find.
Object's own methods are excluded.- Parameters:
clazz- the class to scan, may be null- Returns:
- the methods, subclass overrides first; empty if clazz is null
-
findSlotOverriding
Finds the slot inbucketwhose declarations overridecandidate, ornullif none does.Every declaration in a slot is tested, not just its representative - see
getAllMethods(Class)for why transitivity makes that necessary.- Parameters:
bucket- the slots sharingcandidate's name and parameter countcandidate- the declaration being placed- Returns:
- the slot
candidatebelongs to, or null if it opens a new one
-
overrides
Whethersuboverridessupper JLS 8.4.8.1.Overriding is directional and is not an equivalence relation, so it cannot be expressed as equality of any symmetric key - which is why this predicate exists rather than a comparison against a symmetric signature key. All of the following must hold:
- same name and same parameter types, in order;
sup's declaring class is a proper supertype ofsub's;- neither declaration is
privateorstatic-privatemethods are dispatched withinvokespecialand are not even inherited, andstaticmethods are hidden rather than overridden; - if
supis package-private, the two declaring classes are in the same package. Apublicorprotectedsupcarries no package condition.
Classobjects, so a generic declaration and an override that matches only its erasure are treated as distinct methods, even though a subsignature comparison - and javac - accept them as one:class GenBase<T> { public void take(T t) { } } // erases to take(Object) class GenChild extends GenBase<String> { @Override public void take(String t) { } }overrides(GenChild.take, GenBase.take)returnsfalsehere, and the two are reported as distinct methods even though javac accepts the@Override.There is deliberately no condition on
sub's own access: Java permits an override to widen access, and a package-private method widened topublicby a same-package subclass is a genuine override.Public so that
FinalContractValidatorshares this one implementation instead of re-deriving the rule - two independent copies are how the two consumers drifted apart in the first place (issue #190).Packages are compared by name, matching how the rest of this class treats them; two same-named packages defined by different class loaders are distinct runtime packages to the JVM, a distinction this check does not make.
- Parameters:
sub- the potentially overriding declaration, may be nullsup- the potentially overridden declaration, may be null- Returns:
- true if
suboverridessup; false if either is null
-
isPackagePrivate
public static boolean isPackagePrivate(int modifiers) True for default (package-private) access: neitherpublic,protected, norprivate.Public so that consumers deciding the same question share this implementation. See the note on
overrides(Method, Method)for why a second copy is a hazard rather than a convenience.- Parameters:
modifiers- the modifiers to test, fromMember#getModifiers()- Returns:
- true if the modifiers describe default access
-
packageNameOf
The class's package name, or the empty string for the unnamed package.Public for the same reason as
isPackagePrivate(int): package-private access is decided here and byAopEligibility, and the two must not answer differently.- Parameters:
clazz- the class to inspect- Returns:
- the package name, or the empty string
-