@@ -72,7 +72,7 @@ public class TypeDescriptor implements Serializable {
7272
7373 private final ResolvableType resolvableType ;
7474
75- private final AnnotatedElement annotatedElement ;
75+ private final AnnotatedElementAdapter annotatedElement ;
7676
7777
7878 /**
@@ -82,7 +82,6 @@ public class TypeDescriptor implements Serializable {
8282 * @param methodParameter the method parameter
8383 */
8484 public TypeDescriptor (MethodParameter methodParameter ) {
85- Assert .notNull (methodParameter , "MethodParameter must not be null" );
8685 this .resolvableType = ResolvableType .forMethodParameter (methodParameter );
8786 this .type = this .resolvableType .resolve (methodParameter .getParameterType ());
8887 this .annotatedElement = new AnnotatedElementAdapter (methodParameter .getParameterIndex () == -1 ?
@@ -95,7 +94,6 @@ public TypeDescriptor(MethodParameter methodParameter) {
9594 * @param field the field
9695 */
9796 public TypeDescriptor (Field field ) {
98- Assert .notNull (field , "Field must not be null" );
9997 this .resolvableType = ResolvableType .forField (field );
10098 this .type = this .resolvableType .resolve (field .getType ());
10199 this .annotatedElement = new AnnotatedElementAdapter (field .getAnnotations ());
@@ -121,6 +119,7 @@ public TypeDescriptor(Property property) {
121119 * @param resolvableType the resolvable type
122120 * @param type the backing type (or {@code null} if it should get resolved)
123121 * @param annotations the type annotations
122+ * @since 4.0
124123 */
125124 protected TypeDescriptor (ResolvableType resolvableType , Class <?> type , Annotation [] annotations ) {
126125 this .resolvableType = resolvableType ;
@@ -211,7 +210,7 @@ public TypeDescriptor upcast(Class<?> superType) {
211210 }
212211
213212 /**
214- * Returns the name of this type: the fully qualified class name.
213+ * Return the name of this type: the fully qualified class name.
215214 */
216215 public String getName () {
217216 return ClassUtils .getQualifiedName (getType ());
@@ -225,7 +224,7 @@ public boolean isPrimitive() {
225224 }
226225
227226 /**
228- * The annotations associated with this type descriptor, if any.
227+ * Return the annotations associated with this type descriptor, if any.
229228 * @return the annotations, or an empty array if none
230229 */
231230 public Annotation [] getAnnotations () {
@@ -240,6 +239,11 @@ public Annotation[] getAnnotations() {
240239 * @return <tt>true</tt> if the annotation is present
241240 */
242241 public boolean hasAnnotation (Class <? extends Annotation > annotationType ) {
242+ if (this .annotatedElement .isEmpty ()) {
243+ // Shortcut: AnnotatedElementUtils would have to expect AnnotatedElement.getAnnotations()
244+ // to return a copy of the array, whereas we can do it more efficiently here.
245+ return false ;
246+ }
243247 return AnnotatedElementUtils .isAnnotated (this .annotatedElement , annotationType );
244248 }
245249
@@ -251,6 +255,11 @@ public boolean hasAnnotation(Class<? extends Annotation> annotationType) {
251255 */
252256 @ SuppressWarnings ("unchecked" )
253257 public <T extends Annotation > T getAnnotation (Class <T > annotationType ) {
258+ if (this .annotatedElement .isEmpty ()) {
259+ // Shortcut: AnnotatedElementUtils would have to expect AnnotatedElement.getAnnotations()
260+ // to return a copy of the array, whereas we can do it more efficiently here.
261+ return null ;
262+ }
254263 return AnnotatedElementUtils .getMergedAnnotation (this .annotatedElement , annotationType );
255264 }
256265
@@ -434,37 +443,51 @@ private TypeDescriptor narrow(Object value, TypeDescriptor typeDescriptor) {
434443 }
435444
436445 @ Override
437- public boolean equals (Object obj ) {
438- if (this == obj ) {
446+ public boolean equals (Object other ) {
447+ if (this == other ) {
439448 return true ;
440449 }
441- if (!(obj instanceof TypeDescriptor )) {
450+ if (!(other instanceof TypeDescriptor )) {
442451 return false ;
443452 }
444- TypeDescriptor other = (TypeDescriptor ) obj ;
445- if (! ObjectUtils . nullSafeEquals ( this . type , other . type )) {
453+ TypeDescriptor otherDesc = (TypeDescriptor ) other ;
454+ if (getType () != otherDesc . getType ( )) {
446455 return false ;
447456 }
448- if (getAnnotations (). length != other . getAnnotations (). length ) {
457+ if (! annotationsMatch ( otherDesc ) ) {
449458 return false ;
450459 }
451- for (Annotation ann : getAnnotations ()) {
452- if (!ann .equals (other .getAnnotation (ann .annotationType ()))) {
453- return false ;
454- }
455- }
456460 if (isCollection () || isArray ()) {
457- return ObjectUtils .nullSafeEquals (getElementTypeDescriptor (), other .getElementTypeDescriptor ());
461+ return ObjectUtils .nullSafeEquals (getElementTypeDescriptor (), otherDesc .getElementTypeDescriptor ());
458462 }
459463 else if (isMap ()) {
460- return ObjectUtils .nullSafeEquals (getMapKeyTypeDescriptor (), other .getMapKeyTypeDescriptor ()) &&
461- ObjectUtils .nullSafeEquals (getMapValueTypeDescriptor (), other .getMapValueTypeDescriptor ());
464+ return ( ObjectUtils .nullSafeEquals (getMapKeyTypeDescriptor (), otherDesc .getMapKeyTypeDescriptor ()) &&
465+ ObjectUtils .nullSafeEquals (getMapValueTypeDescriptor (), otherDesc .getMapValueTypeDescriptor () ));
462466 }
463467 else {
464468 return true ;
465469 }
466470 }
467471
472+ private boolean annotationsMatch (TypeDescriptor otherDesc ) {
473+ Annotation [] anns = getAnnotations ();
474+ Annotation [] otherAnns = otherDesc .getAnnotations ();
475+ if (anns == otherAnns ) {
476+ return true ;
477+ }
478+ if (anns .length != otherAnns .length ) {
479+ return false ;
480+ }
481+ if (anns .length > 0 ) {
482+ for (int i = 0 ; i < anns .length ; i ++) {
483+ if (anns [i ] != otherAnns [i ]) {
484+ return false ;
485+ }
486+ }
487+ }
488+ return true ;
489+ }
490+
468491 @ Override
469492 public int hashCode () {
470493 return getType ().hashCode ();
@@ -480,6 +503,20 @@ public String toString() {
480503 return builder .toString ();
481504 }
482505
506+
507+ /**
508+ * Create a new type descriptor for an object.
509+ * <p>Use this factory method to introspect a source object before asking the
510+ * conversion system to convert it to some another type.
511+ * <p>If the provided object is {@code null}, returns {@code null}, else calls
512+ * {@link #valueOf(Class)} to build a TypeDescriptor from the object's class.
513+ * @param source the source object
514+ * @return the type descriptor
515+ */
516+ public static TypeDescriptor forObject (Object source ) {
517+ return (source != null ? valueOf (source .getClass ()) : null );
518+ }
519+
483520 /**
484521 * Create a new type descriptor from the given type.
485522 * <p>Use this to instruct the conversion system to convert an object to a
@@ -640,19 +677,6 @@ public static TypeDescriptor nested(Property property, int nestingLevel) {
640677 return nested (new TypeDescriptor (property ), nestingLevel );
641678 }
642679
643- /**
644- * Create a new type descriptor for an object.
645- * <p>Use this factory method to introspect a source object before asking the
646- * conversion system to convert it to some another type.
647- * <p>If the provided object is {@code null}, returns {@code null}, else calls
648- * {@link #valueOf(Class)} to build a TypeDescriptor from the object's class.
649- * @param source the source object
650- * @return the type descriptor
651- */
652- public static TypeDescriptor forObject (Object source ) {
653- return (source != null ? valueOf (source .getClass ()) : null );
654- }
655-
656680 private static TypeDescriptor nested (TypeDescriptor typeDescriptor , int nestingLevel ) {
657681 ResolvableType nested = typeDescriptor .resolvableType ;
658682 for (int i = 0 ; i < nestingLevel ; i ++) {
@@ -723,6 +747,10 @@ public Annotation[] getDeclaredAnnotations() {
723747 return getAnnotations ();
724748 }
725749
750+ public boolean isEmpty () {
751+ return ObjectUtils .isEmpty (this .annotations );
752+ }
753+
726754 @ Override
727755 public boolean equals (Object other ) {
728756 return (this == other || (other instanceof AnnotatedElementAdapter &&
0 commit comments