Package org.springframework.core.annotation

# Interface MergedAnnotation<A extends Annotation>

Type Parameters:
  `A` - the annotation type

public interface MergedAnnotation<A extends Annotation>
A single merged annotation returned from a `MergedAnnotations` collection. Presents a view onto an annotation where attribute values may have been "merged" from different source values.

Attribute values may be accessed using the various `get` methods. For example, to access an `int` attribute the `getInt(String)` method would be used.

Note that attribute values are not converted when accessed. For example, it is not possible to call `getString(String)` if the underlying attribute is an `int`. The only exception to this rule is `Class` and `Class[]` values which may be accessed as `String` and `String[]` respectively to prevent potential early class initialization.

If necessary, a `MergedAnnotation` can be synthesized back into an actual `Annotation`.

Since:
  5.2
Author:
  Phillip Webb, Juergen Hoeller, Sam Brannen
See Also:
  - `MergedAnnotations`
  - `MergedAnnotationPredicates`

## Nested Class Summary

Nested Classes
Modifier and Type
Interface
Description
`static enum`
`MergedAnnotation.Adapt`
Adaptations that can be applied to attribute values when creating Maps or `AnnotationAttributes`.

## Field Summary

Fields
Modifier and Type
Field
Description
`static final String`
`VALUE`
The attribute name for annotations with a single element.

## Method Summary

Modifier and Type
Method
Description
`AnnotationAttributes`
`asAnnotationAttributes(MergedAnnotation.Adapt... adaptations)`
Create a new mutable `AnnotationAttributes` instance from this merged annotation.
`<T extends Map<String, Object>>
T`
`asMap(Function<MergedAnnotation<?>,T> factory, MergedAnnotation.Adapt... adaptations)`
Create a new `Map` of the given type that contains all the annotation attributes.
`Map<String,Object>`
`asMap(MergedAnnotation.Adapt... adaptations)`
Create an immutable `Map` that contains all the annotation attributes.
`MergedAnnotation<A>`
`filterAttributes(Predicate<String> predicate)`
Create a new view of the annotation with only attributes that match the given predicate.
`MergedAnnotation<A>`
`filterDefaultValues()`
Create a new view of the annotation with all attributes that have default values removed.
`static <A extends Annotation>
MergedAnnotation<A>`
`from(A annotation)`
Create a new `MergedAnnotation` instance from the specified annotation.
`static <A extends Annotation>
MergedAnnotation<A>`
`from(Object source, A annotation)`
Create a new `MergedAnnotation` instance from the specified annotation.
`int`
`getAggregateIndex()`
Get the index of the aggregate collection containing this annotation.
`<T extends Annotation>
MergedAnnotation<T>`
`getAnnotation(String attributeName, Class<T> type)`
Get a required annotation attribute value from the annotation.
`<T extends Annotation>
MergedAnnotation<T>[]`
`getAnnotationArray(String attributeName, Class<T> type)`
Get a required annotation array attribute value from the annotation.
`boolean`
`getBoolean(String attributeName)`
Get a required boolean attribute value from the annotation.
`boolean[]`
`getBooleanArray(String attributeName)`
Get a required boolean array attribute value from the annotation.
`byte`
`getByte(String attributeName)`
Get a required byte attribute value from the annotation.
`byte[]`
`getByteArray(String attributeName)`
Get a required byte array attribute value from the annotation.
`char`
`getChar(String attributeName)`
Get a required char attribute value from the annotation.
`char[]`
`getCharArray(String attributeName)`
Get a required char array attribute value from the annotation.
`Class<?>`
`getClass(String attributeName)`
Get a required class attribute value from the annotation.
`Class<?>[]`
`getClassArray(String attributeName)`
Get a required class array attribute value from the annotation.
`Optional<Object>`
`getDefaultValue(String attributeName)`
Get the default attribute value from the annotation as specified in the annotation declaration.
`<T> Optional<T>`
`getDefaultValue(String attributeName, Class<T> type)`
Get the default attribute value from the annotation as specified in the annotation declaration.
`int`
`getDistance()`
Get the distance of this annotation related to its use as a meta-annotation.
`double`
`getDouble(String attributeName)`
Get a required double attribute value from the annotation.
`double[]`
`getDoubleArray(String attributeName)`
Get a required double array attribute value from the annotation.
`<E extends Enum<E>>
E`
`getEnum(String attributeName, Class<E> type)`
Get a required enum attribute value from the annotation.
`<E extends Enum<E>>
E[]`
`getEnumArray(String attributeName, Class<E> type)`
Get a required enum array attribute value from the annotation.
`float`
`getFloat(String attributeName)`
Get a required float attribute value from the annotation.
`float[]`
`getFloatArray(String attributeName)`
Get a required float array attribute value from the annotation.
`int`
`getInt(String attributeName)`
Get a required int attribute value from the annotation.
`int[]`
`getIntArray(String attributeName)`
Get a required int array attribute value from the annotation.
`long`
`getLong(String attributeName)`
Get a required long attribute value from the annotation.
`long[]`
`getLongArray(String attributeName)`
Get a required long array attribute value from the annotation.
`MergedAnnotation<?>`
`getMetaSource()`
Get the source of the meta-annotation, or `null` if the annotation is not meta-present.
`List<Class<? extends Annotation>>`
`getMetaTypes()`
Get the complete list of annotation types within the annotation hierarchy from this annotation to the `root`.
`MergedAnnotation<?>`
`getRoot()`
Get the root annotation, i.e.
`short`
`getShort(String attributeName)`
Get a required short attribute value from the annotation.
`short[]`
`getShortArray(String attributeName)`
Get a required short array attribute value from the annotation.
`Object`
`getSource()`
Get the source that ultimately declared the root annotation, or `null` if the source is not known.
`String`
`getString(String attributeName)`
Get a required string attribute value from the annotation.
`String[]`
`getStringArray(String attributeName)`
Get a required string array attribute value from the annotation.
`Class<A>`
`getType()`
Get the `Class` reference for the actual annotation type.
`Optional<Object>`
`getValue(String attributeName)`
Get an optional attribute value from the annotation.
`<T> Optional<T>`
`getValue(String attributeName, Class<T> type)`
Get an optional attribute value from the annotation.
`boolean`
`hasDefaultValue(String attributeName)`
Determine if the specified attribute name has a default value when compared to the annotation declaration.
`boolean`
`hasNonDefaultValue(String attributeName)`
Determine if the specified attribute name has a non-default value when compared to the annotation declaration.
`boolean`
`isDirectlyPresent()`
Determine if the annotation is directly present on the source.
`boolean`
`isMetaPresent()`
Determine if the annotation is meta-present on the source.
`boolean`
`isPresent()`
Determine if the annotation is present on the source.
`static <A extends Annotation>
MergedAnnotation<A>`
`missing()`
Create a `MergedAnnotation` that represents a missing annotation (i.e.
`static <A extends Annotation>
MergedAnnotation<A>`
`of(Class<A> annotationType)`
Create a new `MergedAnnotation` instance of the specified annotation type.
`static <A extends Annotation>
MergedAnnotation<A>`
`of(Class<A> annotationType, Map<String,?> attributes)`
Create a new `MergedAnnotation` instance of the specified annotation type with attribute values supplied by a map.
`static <A extends Annotation>
MergedAnnotation<A>`
`of(ClassLoader classLoader, Object source, Class<A> annotationType, Map<String,?> attributes)`
Create a new `MergedAnnotation` instance of the specified annotation type with attribute values supplied by a map.
`static <A extends Annotation>
MergedAnnotation<A>`
`of(AnnotatedElement source, Class<A> annotationType, Map<String,?> attributes)`
Create a new `MergedAnnotation` instance of the specified annotation type with attribute values supplied by a map.
`A`
`synthesize()`
Create a type-safe synthesized version of this merged annotation that can be used directly in code.
`Optional<A>`
`synthesize(Predicate<? super MergedAnnotation<A>> condition)`
Optionally create a type-safe synthesized version of this annotation based on a condition predicate.
`MergedAnnotation<A>`
`withNonMergedAttributes()`
Create a new view of the annotation that exposes non-merged attribute values.

## Field Details

### VALUE

static final String VALUE
The attribute name for annotations with a single element.

See Also:
  - Constant Field Values

## Method Details

### getType

Class<A> getType()
Get the `Class` reference for the actual annotation type.

Returns:
  the annotation type

### isPresent

boolean isPresent()
Determine if the annotation is present on the source. Considers directly present and meta-present annotations within the context of the `MergedAnnotations.SearchStrategy` used.

Returns:
  `true` if the annotation is present

### isDirectlyPresent

boolean isDirectlyPresent()
Determine if the annotation is directly present on the source.

A directly present annotation is one that the user has explicitly declared and not one that is meta-present or `@Inherited`.

Returns:
  `true` if the annotation is directly present

### isMetaPresent

boolean isMetaPresent()
Determine if the annotation is meta-present on the source.

A meta-present annotation is an annotation that the user hasn't explicitly declared, but has been used as a meta-annotation somewhere in the annotation hierarchy.

Returns:
  `true` if the annotation is meta-present

### getDistance

int getDistance()
Get the distance of this annotation related to its use as a meta-annotation.

A directly declared annotation has a distance of `0`, a meta-annotation has a distance of `1`, a meta-annotation on a meta-annotation has a distance of `2`, etc. A missing annotation will always return a distance of `-1`.

Returns:
  the annotation distance or `-1` if the annotation is missing

### getAggregateIndex

int getAggregateIndex()
Get the index of the aggregate collection containing this annotation.

Can be used to reorder a stream of annotations, for example, to give a higher priority to annotations declared on a superclass or interface. A missing annotation will always return an aggregate index of `-1`.

Returns:
  the aggregate index (starting at `0`) or `-1` if the annotation is missing

### getSource

@Nullable Object getSource()
Get the source that ultimately declared the root annotation, or `null` if the source is not known.

If this merged annotation was created `from` an `AnnotatedElement` then this source will be an element of the same type. If the annotation was loaded without using reflection, the source can be of any type, but should have a sensible `toString()`. Meta-annotations will always return the same source as the `root`.

Returns:
  the source, or `null`

### getMetaSource

@Nullable MergedAnnotation<?> getMetaSource()
Get the source of the meta-annotation, or `null` if the annotation is not meta-present.

The meta-source is the annotation that was meta-annotated with this annotation.

Returns:
  the meta-annotation source or `null`
See Also:
  - `getRoot()`

### getRoot

MergedAnnotation<?> getRoot()
Get the root annotation, i.e. the `distance` `0` annotation as directly declared on the source.

Returns:
  the root annotation
See Also:
  - `getMetaSource()`

### getMetaTypes

List<Class<? extends Annotation>> getMetaTypes()
Get the complete list of annotation types within the annotation hierarchy from this annotation to the `root`.

Provides a useful way to uniquely identify a merged annotation instance.

Returns:
  the meta types for the annotation
See Also:
  - `MergedAnnotationPredicates.unique(Function)`
  - `getRoot()`
  - `getMetaSource()`

### hasNonDefaultValue

boolean hasNonDefaultValue(String attributeName)
Determine if the specified attribute name has a non-default value when compared to the annotation declaration.

Parameters:
  `attributeName` - the attribute name
Returns:
  `true` if the attribute value is different from the default value

### hasDefaultValue

boolean hasDefaultValue(String attributeName) throws NoSuchElementException
Determine if the specified attribute name has a default value when compared to the annotation declaration.

Parameters:
  `attributeName` - the attribute name
Returns:
  `true` if the attribute value is the same as the default value
Throws:
  `NoSuchElementException`

### getByte

byte getByte(String attributeName) throws NoSuchElementException
Get a required byte attribute value from the annotation.

Parameters:
  `attributeName` - the attribute name
Returns:
  the value as a byte
Throws:
  `NoSuchElementException` - if there is no matching attribute

### getByteArray

byte[] getByteArray(String attributeName) throws NoSuchElementException
Get a required byte array attribute value from the annotation.

Parameters:
  `attributeName` - the attribute name
Returns:
  the value as a byte array
Throws:
  `NoSuchElementException` - if there is no matching attribute

### getBoolean

boolean getBoolean(String attributeName) throws NoSuchElementException
Get a required boolean attribute value from the annotation.

Parameters:
  `attributeName` - the attribute name
Returns:
  the value as a boolean
Throws:
  `NoSuchElementException` - if there is no matching attribute

### getBooleanArray

boolean[] getBooleanArray(String attributeName) throws NoSuchElementException
Get a required boolean array attribute value from the annotation.

Parameters:
  `attributeName` - the attribute name
Returns:
  the value as a boolean array
Throws:
  `NoSuchElementException` - if there is no matching attribute

### getChar

char getChar(String attributeName) throws NoSuchElementException
Get a required char attribute value from the annotation.

Parameters:
  `attributeName` - the attribute name
Returns:
  the value as a char
Throws:
  `NoSuchElementException` - if there is no matching attribute

### getCharArray

char[] getCharArray(String attributeName) throws NoSuchElementException
Get a required char array attribute value from the annotation.

Parameters:
  `attributeName` - the attribute name
Returns:
  the value as a char array
Throws:
  `NoSuchElementException` - if there is no matching attribute

### getShort

short getShort(String attributeName) throws NoSuchElementException
Get a required short attribute value from the annotation.

Parameters:
  `attributeName` - the attribute name
Returns:
  the value as a short
Throws:
  `NoSuchElementException` - if there is no matching attribute

### getShortArray

short[] getShortArray(String attributeName) throws NoSuchElementException
Get a required short array attribute value from the annotation.

Parameters:
  `attributeName` - the attribute name
Returns:
  the value as a short array
Throws:
  `NoSuchElementException` - if there is no matching attribute

### getInt

int getInt(String attributeName) throws NoSuchElementException
Get a required int attribute value from the annotation.

Parameters:
  `attributeName` - the attribute name
Returns:
  the value as an int
Throws:
  `NoSuchElementException` - if there is no matching attribute

### getIntArray

int[] getIntArray(String attributeName) throws NoSuchElementException
Get a required int array attribute value from the annotation.

Parameters:
  `attributeName` - the attribute name
Returns:
  the value as an int array
Throws:
  `NoSuchElementException` - if there is no matching attribute

### getLong

long getLong(String attributeName) throws NoSuchElementException
Get a required long attribute value from the annotation.

Parameters:
  `attributeName` - the attribute name
Returns:
  the value as a long
Throws:
  `NoSuchElementException` - if there is no matching attribute

### getLongArray

long[] getLongArray(String attributeName) throws NoSuchElementException
Get a required long array attribute value from the annotation.

Parameters:
  `attributeName` - the attribute name
Returns:
  the value as a long array
Throws:
  `NoSuchElementException` - if there is no matching attribute

### getDouble

double getDouble(String attributeName) throws NoSuchElementException
Get a required double attribute value from the annotation.

Parameters:
  `attributeName` - the attribute name
Returns:
  the value as a double
Throws:
  `NoSuchElementException` - if there is no matching attribute

### getDoubleArray

double[] getDoubleArray(String attributeName) throws NoSuchElementException
Get a required double array attribute value from the annotation.

Parameters:
  `attributeName` - the attribute name
Returns:
  the value as a double array
Throws:
  `NoSuchElementException` - if there is no matching attribute

### getFloat

float getFloat(String attributeName) throws NoSuchElementException
Get a required float attribute value from the annotation.

Parameters:
  `attributeName` - the attribute name
Returns:
  the value as a float
Throws:
  `NoSuchElementException` - if there is no matching attribute

### getFloatArray

float[] getFloatArray(String attributeName) throws NoSuchElementException
Get a required float array attribute value from the annotation.

Parameters:
  `attributeName` - the attribute name
Returns:
  the value as a float array
Throws:
  `NoSuchElementException` - if there is no matching attribute

### getString

String getString(String attributeName) throws NoSuchElementException
Get a required string attribute value from the annotation.

Parameters:
  `attributeName` - the attribute name
Returns:
  the value as a string
Throws:
  `NoSuchElementException` - if there is no matching attribute

### getStringArray

String[] getStringArray(String attributeName) throws NoSuchElementException
Get a required string array attribute value from the annotation.

Parameters:
  `attributeName` - the attribute name
Returns:
  the value as a string array
Throws:
  `NoSuchElementException` - if there is no matching attribute

### getClass

Class<?> getClass(String attributeName) throws NoSuchElementException
Get a required class attribute value from the annotation.

Parameters:
  `attributeName` - the attribute name
Returns:
  the value as a class
Throws:
  `NoSuchElementException` - if there is no matching attribute

### getClassArray

Class<?>[] getClassArray(String attributeName) throws NoSuchElementException
Get a required class array attribute value from the annotation.

Parameters:
  `attributeName` - the attribute name
Returns:
  the value as a class array
Throws:
  `NoSuchElementException` - if there is no matching attribute

### getEnum

<E extends Enum<E>> E getEnum(String attributeName, Class<E> type) throws NoSuchElementException
Get a required enum attribute value from the annotation.

Parameters:
  `attributeName` - the attribute name
  `type` - the enum type
Returns:
  the value as an enum
Throws:
  `NoSuchElementException` - if there is no matching attribute

### getEnumArray

<E extends Enum<E>> E[] getEnumArray(String attributeName, Class<E> type) throws NoSuchElementException
Get a required enum array attribute value from the annotation.

Parameters:
  `attributeName` - the attribute name
  `type` - the enum type
Returns:
  the value as an enum array
Throws:
  `NoSuchElementException` - if there is no matching attribute

### getAnnotation

<T extends Annotation> MergedAnnotation<T> getAnnotation(String attributeName, Class<T> type) throws NoSuchElementException
Get a required annotation attribute value from the annotation.

Parameters:
  `attributeName` - the attribute name
  `type` - the annotation type
Returns:
  the value as a `MergedAnnotation`
Throws:
  `NoSuchElementException` - if there is no matching attribute

### getAnnotationArray

<T extends Annotation> MergedAnnotation<T>[] getAnnotationArray(String attributeName, Class<T> type) throws NoSuchElementException
Get a required annotation array attribute value from the annotation.

Parameters:
  `attributeName` - the attribute name
  `type` - the annotation type
Returns:
  the value as a `MergedAnnotation` array
Throws:
  `NoSuchElementException` - if there is no matching attribute

### getValue

Optional<Object> getValue(String attributeName)
Get an optional attribute value from the annotation.

Parameters:
  `attributeName` - the attribute name
Returns:
  an optional value or `Optional.empty()` if there is no matching attribute

### getValue

<T> Optional<T> getValue(String attributeName, Class<T> type)
Get an optional attribute value from the annotation.

Parameters:
  `attributeName` - the attribute name
  `type` - the attribute type. Must be compatible with the underlying attribute type or `Object.class`.
Returns:
  an optional value or `Optional.empty()` if there is no matching attribute

### getDefaultValue

Optional<Object> getDefaultValue(String attributeName)
Get the default attribute value from the annotation as specified in the annotation declaration.

Parameters:
  `attributeName` - the attribute name
Returns:
  an optional of the default value or `Optional.empty()` if there is no matching attribute or no defined default

### getDefaultValue

<T> Optional<T> getDefaultValue(String attributeName, Class<T> type)
Get the default attribute value from the annotation as specified in the annotation declaration.

Parameters:
  `attributeName` - the attribute name
  `type` - the attribute type. Must be compatible with the underlying attribute type or `Object.class`.
Returns:
  an optional of the default value or `Optional.empty()` if there is no matching attribute or no defined default

### filterDefaultValues

MergedAnnotation<A> filterDefaultValues()
Create a new view of the annotation with all attributes that have default values removed.

Returns:
  a filtered view of the annotation without any attributes that have a default value
See Also:
  - `filterAttributes(Predicate)`

### filterAttributes

MergedAnnotation<A> filterAttributes(Predicate<String> predicate)
Create a new view of the annotation with only attributes that match the given predicate.

Parameters:
  `predicate` - a predicate used to filter attribute names
Returns:
  a filtered view of the annotation
See Also:
  - `filterDefaultValues()`
  - `MergedAnnotationPredicates`

### withNonMergedAttributes

MergedAnnotation<A> withNonMergedAttributes()
Create a new view of the annotation that exposes non-merged attribute values.

Methods from this view will return attribute values with only alias mirroring rules applied. Aliases to meta-source attributes will not be applied.

Returns:
  a non-merged view of the annotation

### asAnnotationAttributes

AnnotationAttributes asAnnotationAttributes(MergedAnnotation.Adapt... adaptations)
Create a new mutable `AnnotationAttributes` instance from this merged annotation.

The adaptations may be used to change the way that values are added.

Parameters:
  `adaptations` - the adaptations that should be applied to the annotation values
Returns:
  a mutable `AnnotationAttributes` instance containing the attributes and values

### asMap

Map<String,Object> asMap(MergedAnnotation.Adapt... adaptations)
Create an immutable `Map` that contains all the annotation attributes.

The adaptations may be used to change the way that values are added.

Parameters:
  `adaptations` - the adaptations that should be applied to the annotation values
Returns:
  an immutable map containing the attributes and values

### asMap

<T extends Map<String, Object>> T asMap(Function<MergedAnnotation<?>,T> factory, MergedAnnotation.Adapt... adaptations)
Create a new `Map` of the given type that contains all the annotation attributes.

The adaptations may be used to change the way that values are added.

Parameters:
  `factory` - a map factory
  `adaptations` - the adaptations that should be applied to the annotation values
Returns:
  a map containing the attributes and values

### synthesize

A synthesize() throws NoSuchElementException
Create a type-safe synthesized version of this merged annotation that can be used directly in code.

The result is synthesized using a JDK `Proxy` and as a result may incur a computational cost when first invoked.

If this merged annotation was created from a map of annotation attributes or default attribute values, those attributes will always be synthesized into an annotation instance.

If this merged annotation was created from an annotation instance, that annotation will be returned unmodified if it is not synthesizable. An annotation is considered synthesizable if it has not already been synthesized and one of the following is true.

- The annotation declares attributes annotated with `@AliasFor`.
- The annotation is a composed annotation that relies on convention-based annotation attribute overrides in meta-annotations.
- The annotation declares attributes that are annotations or arrays of annotations that are themselves synthesizable.

Returns:
  a synthesized version of the annotation or the original annotation unmodified
Throws:
  `NoSuchElementException` - on a missing annotation

### synthesize

Optional<A> synthesize(Predicate<? super MergedAnnotation<A>> condition) throws NoSuchElementException
Optionally create a type-safe synthesized version of this annotation based on a condition predicate.

The result is synthesized using a JDK `Proxy` and as a result may incur a computational cost when first invoked.

Consult the documentation for `synthesize()` for an explanation of what is considered synthesizable.

Parameters:
  `condition` - the test to determine if the annotation can be synthesized
Returns:
  an optional containing the synthesized version of the annotation or an empty optional if the condition doesn't match
Throws:
  `NoSuchElementException` - on a missing annotation
See Also:
  - `MergedAnnotationPredicates`

### missing

static <A extends Annotation> MergedAnnotation<A> missing()
Create a `MergedAnnotation` that represents a missing annotation (i.e. one that is not present).

Returns:
  an instance representing a missing annotation

### from

static <A extends Annotation> MergedAnnotation<A> from(A annotation)
Create a new `MergedAnnotation` instance from the specified annotation.

Parameters:
  `annotation` - the annotation to include
Returns:
  a `MergedAnnotation` instance containing the annotation

### from

static <A extends Annotation> MergedAnnotation<A> from(@Nullable Object source, A annotation)
Create a new `MergedAnnotation` instance from the specified annotation.

Parameters:
  `source` - the source for the annotation. This source is used only for information and logging. It does not need to actually contain the specified annotations, and it will not be searched.
  `annotation` - the annotation to include
Returns:
  a `MergedAnnotation` instance for the annotation

### of

static <A extends Annotation> MergedAnnotation<A> of(Class<A> annotationType)
Create a new `MergedAnnotation` instance of the specified annotation type. The resulting annotation will not have any attribute values but may still be used to query default values.

Parameters:
  `annotationType` - the annotation type
Returns:
  a `MergedAnnotation` instance for the annotation

### of

static <A extends Annotation> MergedAnnotation<A> of(Class<A> annotationType, @Nullable Map<String,?> attributes)
Create a new `MergedAnnotation` instance of the specified annotation type with attribute values supplied by a map.

Parameters:
  `annotationType` - the annotation type
  `attributes` - the annotation attributes or `null` if just default values should be used
Returns:
  a `MergedAnnotation` instance for the annotation and attributes
See Also:
  - `of(AnnotatedElement, Class, Map)`

### of

static <A extends Annotation> MergedAnnotation<A> of(@Nullable AnnotatedElement source, Class<A> annotationType, @Nullable Map<String,?> attributes)
Create a new `MergedAnnotation` instance of the specified annotation type with attribute values supplied by a map.

Parameters:
  `source` - the source for the annotation. This source is used only for information and logging. It does not need to actually contain the specified annotations and it will not be searched.
  `annotationType` - the annotation type
  `attributes` - the annotation attributes or `null` if just default values should be used
Returns:
  a `MergedAnnotation` instance for the annotation and attributes

### of

static <A extends Annotation> MergedAnnotation<A> of(@Nullable ClassLoader classLoader, @Nullable Object source, Class<A> annotationType, @Nullable Map<String,?> attributes)
Create a new `MergedAnnotation` instance of the specified annotation type with attribute values supplied by a map.

Parameters:
  `classLoader` - the class loader used to resolve class attributes
  `source` - the source for the annotation. This source is used only for information and logging. It does not need to actually contain the specified annotations and it will not be searched.
  `annotationType` - the annotation type
  `attributes` - the annotation attributes or `null` if just default values should be used
Returns:
  a `MergedAnnotation` instance for the annotation and attributes