/*
 * Copyright 2002-2020 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.springframework.core;

import java.lang.annotation.Annotation;
import java.lang.reflect.AnnotatedElement;
import java.lang.reflect.Constructor;
import java.lang.reflect.Executable;
import java.lang.reflect.Member;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
import java.util.function.Predicate;

import kotlin.Unit;
import kotlin.reflect.KFunction;
import kotlin.reflect.KParameter;
import kotlin.reflect.jvm.ReflectJvmMapping;

import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
import org.springframework.util.ObjectUtils;

Helper class that encapsulates the specification of a method parameter, i.e. a Method or Constructor plus a parameter index and a nested type index for a declared generic type. Useful as a specification object to pass along.

As of 4.2, there is a SynthesizingMethodParameter subclass available which synthesizes annotations with attribute aliases. That subclass is used for web and message endpoint processing, in particular.

Author:Juergen Hoeller, Rob Harrop, Andy Clement, Sam Brannen, Sebastien Deleuze, Phillip Webb
See Also:
Since:2.0
/** * Helper class that encapsulates the specification of a method parameter, i.e. a {@link Method} * or {@link Constructor} plus a parameter index and a nested type index for a declared generic * type. Useful as a specification object to pass along. * * <p>As of 4.2, there is a {@link org.springframework.core.annotation.SynthesizingMethodParameter} * subclass available which synthesizes annotations with attribute aliases. That subclass is used * for web and message endpoint processing, in particular. * * @author Juergen Hoeller * @author Rob Harrop * @author Andy Clement * @author Sam Brannen * @author Sebastien Deleuze * @author Phillip Webb * @since 2.0 * @see org.springframework.core.annotation.SynthesizingMethodParameter */
public class MethodParameter { private static final Annotation[] EMPTY_ANNOTATION_ARRAY = new Annotation[0]; private final Executable executable; private final int parameterIndex; @Nullable private volatile Parameter parameter; private int nestingLevel;
Map from Integer level to Integer type index.
/** Map from Integer level to Integer type index. */
@Nullable Map<Integer, Integer> typeIndexesPerLevel;
The containing class. Could also be supplied by overriding getContainingClass()
/** The containing class. Could also be supplied by overriding {@link #getContainingClass()} */
@Nullable private volatile Class<?> containingClass; @Nullable private volatile Class<?> parameterType; @Nullable private volatile Type genericParameterType; @Nullable private volatile Annotation[] parameterAnnotations; @Nullable private volatile ParameterNameDiscoverer parameterNameDiscoverer; @Nullable private volatile String parameterName; @Nullable private volatile MethodParameter nestedMethodParameter;
Create a new MethodParameter for the given method, with nesting level 1.
Params:
  • method – the Method to specify a parameter for
  • parameterIndex – the index of the parameter: -1 for the method return type; 0 for the first method parameter; 1 for the second method parameter, etc.
/** * Create a new {@code MethodParameter} for the given method, with nesting level 1. * @param method the Method to specify a parameter for * @param parameterIndex the index of the parameter: -1 for the method * return type; 0 for the first method parameter; 1 for the second method * parameter, etc. */
public MethodParameter(Method method, int parameterIndex) { this(method, parameterIndex, 1); }
Create a new MethodParameter for the given method.
Params:
  • method – the Method to specify a parameter for
  • parameterIndex – the index of the parameter: -1 for the method return type; 0 for the first method parameter; 1 for the second method parameter, etc.
  • nestingLevel – the nesting level of the target type (typically 1; e.g. in case of a List of Lists, 1 would indicate the nested List, whereas 2 would indicate the element of the nested List)
/** * Create a new {@code MethodParameter} for the given method. * @param method the Method to specify a parameter for * @param parameterIndex the index of the parameter: -1 for the method * return type; 0 for the first method parameter; 1 for the second method * parameter, etc. * @param nestingLevel the nesting level of the target type * (typically 1; e.g. in case of a List of Lists, 1 would indicate the * nested List, whereas 2 would indicate the element of the nested List) */
public MethodParameter(Method method, int parameterIndex, int nestingLevel) { Assert.notNull(method, "Method must not be null"); this.executable = method; this.parameterIndex = validateIndex(method, parameterIndex); this.nestingLevel = nestingLevel; }
Create a new MethodParameter for the given constructor, with nesting level 1.
Params:
  • constructor – the Constructor to specify a parameter for
  • parameterIndex – the index of the parameter
/** * Create a new MethodParameter for the given constructor, with nesting level 1. * @param constructor the Constructor to specify a parameter for * @param parameterIndex the index of the parameter */
public MethodParameter(Constructor<?> constructor, int parameterIndex) { this(constructor, parameterIndex, 1); }
Create a new MethodParameter for the given constructor.
Params:
  • constructor – the Constructor to specify a parameter for
  • parameterIndex – the index of the parameter
  • nestingLevel – the nesting level of the target type (typically 1; e.g. in case of a List of Lists, 1 would indicate the nested List, whereas 2 would indicate the element of the nested List)
/** * Create a new MethodParameter for the given constructor. * @param constructor the Constructor to specify a parameter for * @param parameterIndex the index of the parameter * @param nestingLevel the nesting level of the target type * (typically 1; e.g. in case of a List of Lists, 1 would indicate the * nested List, whereas 2 would indicate the element of the nested List) */
public MethodParameter(Constructor<?> constructor, int parameterIndex, int nestingLevel) { Assert.notNull(constructor, "Constructor must not be null"); this.executable = constructor; this.parameterIndex = validateIndex(constructor, parameterIndex); this.nestingLevel = nestingLevel; }
Internal constructor used to create a MethodParameter with a containing class already set.
Params:
  • executable – the Executable to specify a parameter for
  • parameterIndex – the index of the parameter
  • containingClass – the containing class
Since:5.2
/** * Internal constructor used to create a {@link MethodParameter} with a * containing class already set. * @param executable the Executable to specify a parameter for * @param parameterIndex the index of the parameter * @param containingClass the containing class * @since 5.2 */
MethodParameter(Executable executable, int parameterIndex, @Nullable Class<?> containingClass) { Assert.notNull(executable, "Executable must not be null"); this.executable = executable; this.parameterIndex = validateIndex(executable, parameterIndex); this.nestingLevel = 1; this.containingClass = containingClass; }
Copy constructor, resulting in an independent MethodParameter object based on the same metadata and cache state that the original object was in.
Params:
  • original – the original MethodParameter object to copy from
/** * Copy constructor, resulting in an independent MethodParameter object * based on the same metadata and cache state that the original object was in. * @param original the original MethodParameter object to copy from */
public MethodParameter(MethodParameter original) { Assert.notNull(original, "Original must not be null"); this.executable = original.executable; this.parameterIndex = original.parameterIndex; this.parameter = original.parameter; this.nestingLevel = original.nestingLevel; this.typeIndexesPerLevel = original.typeIndexesPerLevel; this.containingClass = original.containingClass; this.parameterType = original.parameterType; this.genericParameterType = original.genericParameterType; this.parameterAnnotations = original.parameterAnnotations; this.parameterNameDiscoverer = original.parameterNameDiscoverer; this.parameterName = original.parameterName; }
Return the wrapped Method, if any.

Note: Either Method or Constructor is available.

Returns:the Method, or null if none
/** * Return the wrapped Method, if any. * <p>Note: Either Method or Constructor is available. * @return the Method, or {@code null} if none */
@Nullable public Method getMethod() { return (this.executable instanceof Method ? (Method) this.executable : null); }
Return the wrapped Constructor, if any.

Note: Either Method or Constructor is available.

Returns:the Constructor, or null if none
/** * Return the wrapped Constructor, if any. * <p>Note: Either Method or Constructor is available. * @return the Constructor, or {@code null} if none */
@Nullable public Constructor<?> getConstructor() { return (this.executable instanceof Constructor ? (Constructor<?>) this.executable : null); }
Return the class that declares the underlying Method or Constructor.
/** * Return the class that declares the underlying Method or Constructor. */
public Class<?> getDeclaringClass() { return this.executable.getDeclaringClass(); }
Return the wrapped member.
Returns:the Method or Constructor as Member
/** * Return the wrapped member. * @return the Method or Constructor as Member */
public Member getMember() { return this.executable; }
Return the wrapped annotated element.

Note: This method exposes the annotations declared on the method/constructor itself (i.e. at the method/constructor level, not at the parameter level).

Returns:the Method or Constructor as AnnotatedElement
/** * Return the wrapped annotated element. * <p>Note: This method exposes the annotations declared on the method/constructor * itself (i.e. at the method/constructor level, not at the parameter level). * @return the Method or Constructor as AnnotatedElement */
public AnnotatedElement getAnnotatedElement() { return this.executable; }
Return the wrapped executable.
Returns:the Method or Constructor as Executable
Since:5.0
/** * Return the wrapped executable. * @return the Method or Constructor as Executable * @since 5.0 */
public Executable getExecutable() { return this.executable; }
Return the Parameter descriptor for method/constructor parameter.
Since:5.0
/** * Return the {@link Parameter} descriptor for method/constructor parameter. * @since 5.0 */
public Parameter getParameter() { if (this.parameterIndex < 0) { throw new IllegalStateException("Cannot retrieve Parameter descriptor for method return type"); } Parameter parameter = this.parameter; if (parameter == null) { parameter = getExecutable().getParameters()[this.parameterIndex]; this.parameter = parameter; } return parameter; }
Return the index of the method/constructor parameter.
Returns:the parameter index (-1 in case of the return type)
/** * Return the index of the method/constructor parameter. * @return the parameter index (-1 in case of the return type) */
public int getParameterIndex() { return this.parameterIndex; }
Increase this parameter's nesting level.
See Also:
  • getNestingLevel()
Deprecated:since 5.2 in favor of nested(Integer)
/** * Increase this parameter's nesting level. * @see #getNestingLevel() * @deprecated since 5.2 in favor of {@link #nested(Integer)} */
@Deprecated public void increaseNestingLevel() { this.nestingLevel++; }
Decrease this parameter's nesting level.
See Also:
  • getNestingLevel()
Deprecated:since 5.2 in favor of retaining the original MethodParameter and using nested(Integer) if nesting is required
/** * Decrease this parameter's nesting level. * @see #getNestingLevel() * @deprecated since 5.2 in favor of retaining the original MethodParameter and * using {@link #nested(Integer)} if nesting is required */
@Deprecated public void decreaseNestingLevel() { getTypeIndexesPerLevel().remove(this.nestingLevel); this.nestingLevel--; }
Return the nesting level of the target type (typically 1; e.g. in case of a List of Lists, 1 would indicate the nested List, whereas 2 would indicate the element of the nested List).
/** * Return the nesting level of the target type * (typically 1; e.g. in case of a List of Lists, 1 would indicate the * nested List, whereas 2 would indicate the element of the nested List). */
public int getNestingLevel() { return this.nestingLevel; }
Return a variant of this MethodParameter with the type for the current level set to the specified value.
Params:
  • typeIndex – the new type index
Since:5.2
/** * Return a variant of this {@code MethodParameter} with the type * for the current level set to the specified value. * @param typeIndex the new type index * @since 5.2 */
public MethodParameter withTypeIndex(int typeIndex) { return nested(this.nestingLevel, typeIndex); }
Set the type index for the current nesting level.
Params:
  • typeIndex – the corresponding type index (or null for the default type index)
See Also:
Deprecated:since 5.2 in favor of withTypeIndex
/** * Set the type index for the current nesting level. * @param typeIndex the corresponding type index * (or {@code null} for the default type index) * @see #getNestingLevel() * @deprecated since 5.2 in favor of {@link #withTypeIndex} */
@Deprecated public void setTypeIndexForCurrentLevel(int typeIndex) { getTypeIndexesPerLevel().put(this.nestingLevel, typeIndex); }
Return the type index for the current nesting level.
See Also:
Returns:the corresponding type index, or null if none specified (indicating the default type index)
/** * Return the type index for the current nesting level. * @return the corresponding type index, or {@code null} * if none specified (indicating the default type index) * @see #getNestingLevel() */
@Nullable public Integer getTypeIndexForCurrentLevel() { return getTypeIndexForLevel(this.nestingLevel); }
Return the type index for the specified nesting level.
Params:
  • nestingLevel – the nesting level to check
Returns:the corresponding type index, or null if none specified (indicating the default type index)
/** * Return the type index for the specified nesting level. * @param nestingLevel the nesting level to check * @return the corresponding type index, or {@code null} * if none specified (indicating the default type index) */
@Nullable public Integer getTypeIndexForLevel(int nestingLevel) { return getTypeIndexesPerLevel().get(nestingLevel); }
Obtain the (lazily constructed) type-indexes-per-level Map.
/** * Obtain the (lazily constructed) type-indexes-per-level Map. */
private Map<Integer, Integer> getTypeIndexesPerLevel() { if (this.typeIndexesPerLevel == null) { this.typeIndexesPerLevel = new HashMap<>(4); } return this.typeIndexesPerLevel; }
Return a variant of this MethodParameter which points to the same parameter but one nesting level deeper.
Since:4.3
/** * Return a variant of this {@code MethodParameter} which points to the * same parameter but one nesting level deeper. * @since 4.3 */
public MethodParameter nested() { return nested(null); }
Return a variant of this MethodParameter which points to the same parameter but one nesting level deeper.
Params:
  • typeIndex – the type index for the new nesting level
Since:5.2
/** * Return a variant of this {@code MethodParameter} which points to the * same parameter but one nesting level deeper. * @param typeIndex the type index for the new nesting level * @since 5.2 */
public MethodParameter nested(@Nullable Integer typeIndex) { MethodParameter nestedParam = this.nestedMethodParameter; if (nestedParam != null && typeIndex == null) { return nestedParam; } nestedParam = nested(this.nestingLevel + 1, typeIndex); if (typeIndex == null) { this.nestedMethodParameter = nestedParam; } return nestedParam; } private MethodParameter nested(int nestingLevel, @Nullable Integer typeIndex) { MethodParameter copy = clone(); copy.nestingLevel = nestingLevel; if (this.typeIndexesPerLevel != null) { copy.typeIndexesPerLevel = new HashMap<>(this.typeIndexesPerLevel); } if (typeIndex != null) { copy.getTypeIndexesPerLevel().put(copy.nestingLevel, typeIndex); } copy.parameterType = null; copy.genericParameterType = null; return copy; }
Return whether this method indicates a parameter which is not required: either in the form of Java 8's Optional, any variant of a parameter-level Nullable annotation (such as from JSR-305 or the FindBugs set of annotations), or a language-level nullable type declaration or Continuation parameter in Kotlin.
Since:4.3
/** * Return whether this method indicates a parameter which is not required: * either in the form of Java 8's {@link java.util.Optional}, any variant * of a parameter-level {@code Nullable} annotation (such as from JSR-305 * or the FindBugs set of annotations), or a language-level nullable type * declaration or {@code Continuation} parameter in Kotlin. * @since 4.3 */
public boolean isOptional() { return (getParameterType() == Optional.class || hasNullableAnnotation() || (KotlinDetector.isKotlinReflectPresent() && KotlinDetector.isKotlinType(getContainingClass()) && KotlinDelegate.isOptional(this))); }
Check whether this method parameter is annotated with any variant of a Nullable annotation, e.g. javax.annotation.Nullable or edu.umd.cs.findbugs.annotations.Nullable.
/** * Check whether this method parameter is annotated with any variant of a * {@code Nullable} annotation, e.g. {@code javax.annotation.Nullable} or * {@code edu.umd.cs.findbugs.annotations.Nullable}. */
private boolean hasNullableAnnotation() { for (Annotation ann : getParameterAnnotations()) { if ("Nullable".equals(ann.annotationType().getSimpleName())) { return true; } } return false; }
Return a variant of this MethodParameter which points to the same parameter but one nesting level deeper in case of a Optional declaration.
See Also:
Since:4.3
/** * Return a variant of this {@code MethodParameter} which points to * the same parameter but one nesting level deeper in case of a * {@link java.util.Optional} declaration. * @since 4.3 * @see #isOptional() * @see #nested() */
public MethodParameter nestedIfOptional() { return (getParameterType() == Optional.class ? nested() : this); }
Return a variant of this MethodParameter which refers to the given containing class.
Params:
  • containingClass – a specific containing class (potentially a subclass of the declaring class, e.g. substituting a type variable)
See Also:
Since:5.2
/** * Return a variant of this {@code MethodParameter} which refers to the * given containing class. * @param containingClass a specific containing class (potentially a * subclass of the declaring class, e.g. substituting a type variable) * @since 5.2 * @see #getParameterType() */
public MethodParameter withContainingClass(@Nullable Class<?> containingClass) { MethodParameter result = clone(); result.containingClass = containingClass; result.parameterType = null; return result; }
Set a containing class to resolve the parameter type against.
/** * Set a containing class to resolve the parameter type against. */
@Deprecated void setContainingClass(Class<?> containingClass) { this.containingClass = containingClass; this.parameterType = null; }
Return the containing class for this method parameter.
See Also:
Returns:a specific containing class (potentially a subclass of the declaring class), or otherwise simply the declaring class itself
/** * Return the containing class for this method parameter. * @return a specific containing class (potentially a subclass of the * declaring class), or otherwise simply the declaring class itself * @see #getDeclaringClass() */
public Class<?> getContainingClass() { Class<?> containingClass = this.containingClass; return (containingClass != null ? containingClass : getDeclaringClass()); }
Set a resolved (generic) parameter type.
/** * Set a resolved (generic) parameter type. */
@Deprecated void setParameterType(@Nullable Class<?> parameterType) { this.parameterType = parameterType; }
Return the type of the method/constructor parameter.
Returns:the parameter type (never null)
/** * Return the type of the method/constructor parameter. * @return the parameter type (never {@code null}) */
public Class<?> getParameterType() { Class<?> paramType = this.parameterType; if (paramType != null) { return paramType; } if (getContainingClass() != getDeclaringClass()) { paramType = ResolvableType.forMethodParameter(this, null, 1).resolve(); } if (paramType == null) { paramType = computeParameterType(); } this.parameterType = paramType; return paramType; }
Return the generic type of the method/constructor parameter.
Returns:the parameter type (never null)
Since:3.0
/** * Return the generic type of the method/constructor parameter. * @return the parameter type (never {@code null}) * @since 3.0 */
public Type getGenericParameterType() { Type paramType = this.genericParameterType; if (paramType == null) { if (this.parameterIndex < 0) { Method method = getMethod(); paramType = (method != null ? (KotlinDetector.isKotlinReflectPresent() && KotlinDetector.isKotlinType(getContainingClass()) ? KotlinDelegate.getGenericReturnType(method) : method.getGenericReturnType()) : void.class); } else { Type[] genericParameterTypes = this.executable.getGenericParameterTypes(); int index = this.parameterIndex; if (this.executable instanceof Constructor && ClassUtils.isInnerClass(this.executable.getDeclaringClass()) && genericParameterTypes.length == this.executable.getParameterCount() - 1) { // Bug in javac: type array excludes enclosing instance parameter // for inner classes with at least one generic constructor parameter, // so access it with the actual parameter index lowered by 1 index = this.parameterIndex - 1; } paramType = (index >= 0 && index < genericParameterTypes.length ? genericParameterTypes[index] : computeParameterType()); } this.genericParameterType = paramType; } return paramType; } private Class<?> computeParameterType() { if (this.parameterIndex < 0) { Method method = getMethod(); if (method == null) { return void.class; } if (KotlinDetector.isKotlinReflectPresent() && KotlinDetector.isKotlinType(getContainingClass())) { return KotlinDelegate.getReturnType(method); } return method.getReturnType(); } return this.executable.getParameterTypes()[this.parameterIndex]; }
Return the nested type of the method/constructor parameter.
See Also:
Returns:the parameter type (never null)
Since:3.1
/** * Return the nested type of the method/constructor parameter. * @return the parameter type (never {@code null}) * @since 3.1 * @see #getNestingLevel() */
public Class<?> getNestedParameterType() { if (this.nestingLevel > 1) { Type type = getGenericParameterType(); for (int i = 2; i <= this.nestingLevel; i++) { if (type instanceof ParameterizedType) { Type[] args = ((ParameterizedType) type).getActualTypeArguments(); Integer index = getTypeIndexForLevel(i); type = args[index != null ? index : args.length - 1]; } // TODO: Object.class if unresolvable } if (type instanceof Class) { return (Class<?>) type; } else if (type instanceof ParameterizedType) { Type arg = ((ParameterizedType) type).getRawType(); if (arg instanceof Class) { return (Class<?>) arg; } } return Object.class; } else { return getParameterType(); } }
Return the nested generic type of the method/constructor parameter.
See Also:
Returns:the parameter type (never null)
Since:4.2
/** * Return the nested generic type of the method/constructor parameter. * @return the parameter type (never {@code null}) * @since 4.2 * @see #getNestingLevel() */
public Type getNestedGenericParameterType() { if (this.nestingLevel > 1) { Type type = getGenericParameterType(); for (int i = 2; i <= this.nestingLevel; i++) { if (type instanceof ParameterizedType) { Type[] args = ((ParameterizedType) type).getActualTypeArguments(); Integer index = getTypeIndexForLevel(i); type = args[index != null ? index : args.length - 1]; } } return type; } else { return getGenericParameterType(); } }
Return the annotations associated with the target method/constructor itself.
/** * Return the annotations associated with the target method/constructor itself. */
public Annotation[] getMethodAnnotations() { return adaptAnnotationArray(getAnnotatedElement().getAnnotations()); }
Return the method/constructor annotation of the given type, if available.
Params:
  • annotationType – the annotation type to look for
Returns:the annotation object, or null if not found
/** * Return the method/constructor annotation of the given type, if available. * @param annotationType the annotation type to look for * @return the annotation object, or {@code null} if not found */
@Nullable public <A extends Annotation> A getMethodAnnotation(Class<A> annotationType) { A annotation = getAnnotatedElement().getAnnotation(annotationType); return (annotation != null ? adaptAnnotation(annotation) : null); }
Return whether the method/constructor is annotated with the given type.
Params:
  • annotationType – the annotation type to look for
See Also:
Since:4.3
/** * Return whether the method/constructor is annotated with the given type. * @param annotationType the annotation type to look for * @since 4.3 * @see #getMethodAnnotation(Class) */
public <A extends Annotation> boolean hasMethodAnnotation(Class<A> annotationType) { return getAnnotatedElement().isAnnotationPresent(annotationType); }
Return the annotations associated with the specific method/constructor parameter.
/** * Return the annotations associated with the specific method/constructor parameter. */
public Annotation[] getParameterAnnotations() { Annotation[] paramAnns = this.parameterAnnotations; if (paramAnns == null) { Annotation[][] annotationArray = this.executable.getParameterAnnotations(); int index = this.parameterIndex; if (this.executable instanceof Constructor && ClassUtils.isInnerClass(this.executable.getDeclaringClass()) && annotationArray.length == this.executable.getParameterCount() - 1) { // Bug in javac in JDK <9: annotation array excludes enclosing instance parameter // for inner classes, so access it with the actual parameter index lowered by 1 index = this.parameterIndex - 1; } paramAnns = (index >= 0 && index < annotationArray.length ? adaptAnnotationArray(annotationArray[index]) : EMPTY_ANNOTATION_ARRAY); this.parameterAnnotations = paramAnns; } return paramAnns; }
Return true if the parameter has at least one annotation, false if it has none.
See Also:
/** * Return {@code true} if the parameter has at least one annotation, * {@code false} if it has none. * @see #getParameterAnnotations() */
public boolean hasParameterAnnotations() { return (getParameterAnnotations().length != 0); }
Return the parameter annotation of the given type, if available.
Params:
  • annotationType – the annotation type to look for
Returns:the annotation object, or null if not found
/** * Return the parameter annotation of the given type, if available. * @param annotationType the annotation type to look for * @return the annotation object, or {@code null} if not found */
@SuppressWarnings("unchecked") @Nullable public <A extends Annotation> A getParameterAnnotation(Class<A> annotationType) { Annotation[] anns = getParameterAnnotations(); for (Annotation ann : anns) { if (annotationType.isInstance(ann)) { return (A) ann; } } return null; }
Return whether the parameter is declared with the given annotation type.
Params:
  • annotationType – the annotation type to look for
See Also:
/** * Return whether the parameter is declared with the given annotation type. * @param annotationType the annotation type to look for * @see #getParameterAnnotation(Class) */
public <A extends Annotation> boolean hasParameterAnnotation(Class<A> annotationType) { return (getParameterAnnotation(annotationType) != null); }
Initialize parameter name discovery for this method parameter.

This method does not actually try to retrieve the parameter name at this point; it just allows discovery to happen when the application calls getParameterName() (if ever).

/** * Initialize parameter name discovery for this method parameter. * <p>This method does not actually try to retrieve the parameter name at * this point; it just allows discovery to happen when the application calls * {@link #getParameterName()} (if ever). */
public void initParameterNameDiscovery(@Nullable ParameterNameDiscoverer parameterNameDiscoverer) { this.parameterNameDiscoverer = parameterNameDiscoverer; }
Return the name of the method/constructor parameter.
Returns:the parameter name (may be null if no parameter name metadata is contained in the class file or no ParameterNameDiscoverer has been set to begin with)
/** * Return the name of the method/constructor parameter. * @return the parameter name (may be {@code null} if no * parameter name metadata is contained in the class file or no * {@link #initParameterNameDiscovery ParameterNameDiscoverer} * has been set to begin with) */
@Nullable public String getParameterName() { if (this.parameterIndex < 0) { return null; } ParameterNameDiscoverer discoverer = this.parameterNameDiscoverer; if (discoverer != null) { String[] parameterNames = null; if (this.executable instanceof Method) { parameterNames = discoverer.getParameterNames((Method) this.executable); } else if (this.executable instanceof Constructor) { parameterNames = discoverer.getParameterNames((Constructor<?>) this.executable); } if (parameterNames != null) { this.parameterName = parameterNames[this.parameterIndex]; } this.parameterNameDiscoverer = null; } return this.parameterName; }
A template method to post-process a given annotation instance before returning it to the caller.

The default implementation simply returns the given annotation as-is.

Params:
  • annotation – the annotation about to be returned
Returns:the post-processed annotation (or simply the original one)
Since:4.2
/** * A template method to post-process a given annotation instance before * returning it to the caller. * <p>The default implementation simply returns the given annotation as-is. * @param annotation the annotation about to be returned * @return the post-processed annotation (or simply the original one) * @since 4.2 */
protected <A extends Annotation> A adaptAnnotation(A annotation) { return annotation; }
A template method to post-process a given annotation array before returning it to the caller.

The default implementation simply returns the given annotation array as-is.

Params:
  • annotations – the annotation array about to be returned
Returns:the post-processed annotation array (or simply the original one)
Since:4.2
/** * A template method to post-process a given annotation array before * returning it to the caller. * <p>The default implementation simply returns the given annotation array as-is. * @param annotations the annotation array about to be returned * @return the post-processed annotation array (or simply the original one) * @since 4.2 */
protected Annotation[] adaptAnnotationArray(Annotation[] annotations) { return annotations; } @Override public boolean equals(@Nullable Object other) { if (this == other) { return true; } if (!(other instanceof MethodParameter)) { return false; } MethodParameter otherParam = (MethodParameter) other; return (getContainingClass() == otherParam.getContainingClass() && ObjectUtils.nullSafeEquals(this.typeIndexesPerLevel, otherParam.typeIndexesPerLevel) && this.nestingLevel == otherParam.nestingLevel && this.parameterIndex == otherParam.parameterIndex && this.executable.equals(otherParam.executable)); } @Override public int hashCode() { return (31 * this.executable.hashCode() + this.parameterIndex); } @Override public String toString() { Method method = getMethod(); return (method != null ? "method '" + method.getName() + "'" : "constructor") + " parameter " + this.parameterIndex; } @Override public MethodParameter clone() { return new MethodParameter(this); }
Create a new MethodParameter for the given method or constructor.

This is a convenience factory method for scenarios where a Method or Constructor reference is treated in a generic fashion.

Params:
  • methodOrConstructor – the Method or Constructor to specify a parameter for
  • parameterIndex – the index of the parameter
Returns:the corresponding MethodParameter instance
Deprecated:as of 5.0, in favor of forExecutable
/** * Create a new MethodParameter for the given method or constructor. * <p>This is a convenience factory method for scenarios where a * Method or Constructor reference is treated in a generic fashion. * @param methodOrConstructor the Method or Constructor to specify a parameter for * @param parameterIndex the index of the parameter * @return the corresponding MethodParameter instance * @deprecated as of 5.0, in favor of {@link #forExecutable} */
@Deprecated public static MethodParameter forMethodOrConstructor(Object methodOrConstructor, int parameterIndex) { if (!(methodOrConstructor instanceof Executable)) { throw new IllegalArgumentException( "Given object [" + methodOrConstructor + "] is neither a Method nor a Constructor"); } return forExecutable((Executable) methodOrConstructor, parameterIndex); }
Create a new MethodParameter for the given method or constructor.

This is a convenience factory method for scenarios where a Method or Constructor reference is treated in a generic fashion.

Params:
  • executable – the Method or Constructor to specify a parameter for
  • parameterIndex – the index of the parameter
Returns:the corresponding MethodParameter instance
Since:5.0
/** * Create a new MethodParameter for the given method or constructor. * <p>This is a convenience factory method for scenarios where a * Method or Constructor reference is treated in a generic fashion. * @param executable the Method or Constructor to specify a parameter for * @param parameterIndex the index of the parameter * @return the corresponding MethodParameter instance * @since 5.0 */
public static MethodParameter forExecutable(Executable executable, int parameterIndex) { if (executable instanceof Method) { return new MethodParameter((Method) executable, parameterIndex); } else if (executable instanceof Constructor) { return new MethodParameter((Constructor<?>) executable, parameterIndex); } else { throw new IllegalArgumentException("Not a Method/Constructor: " + executable); } }
Create a new MethodParameter for the given parameter descriptor.

This is a convenience factory method for scenarios where a Java 8 Parameter descriptor is already available.

Params:
  • parameter – the parameter descriptor
Returns:the corresponding MethodParameter instance
Since:5.0
/** * Create a new MethodParameter for the given parameter descriptor. * <p>This is a convenience factory method for scenarios where a * Java 8 {@link Parameter} descriptor is already available. * @param parameter the parameter descriptor * @return the corresponding MethodParameter instance * @since 5.0 */
public static MethodParameter forParameter(Parameter parameter) { return forExecutable(parameter.getDeclaringExecutable(), findParameterIndex(parameter)); } protected static int findParameterIndex(Parameter parameter) { Executable executable = parameter.getDeclaringExecutable(); Parameter[] allParams = executable.getParameters(); // Try first with identity checks for greater performance. for (int i = 0; i < allParams.length; i++) { if (parameter == allParams[i]) { return i; } } // Potentially try again with object equality checks in order to avoid race // conditions while invoking java.lang.reflect.Executable.getParameters(). for (int i = 0; i < allParams.length; i++) { if (parameter.equals(allParams[i])) { return i; } } throw new IllegalArgumentException("Given parameter [" + parameter + "] does not match any parameter in the declaring executable"); } private static int validateIndex(Executable executable, int parameterIndex) { int count = executable.getParameterCount(); Assert.isTrue(parameterIndex >= -1 && parameterIndex < count, () -> "Parameter index needs to be between -1 and " + (count - 1)); return parameterIndex; }
Inner class to avoid a hard dependency on Kotlin at runtime.
/** * Inner class to avoid a hard dependency on Kotlin at runtime. */
private static class KotlinDelegate {
Check whether the specified MethodParameter represents a nullable Kotlin type, an optional parameter (with a default value in the Kotlin declaration) or a Continuation parameter used in suspending functions.
/** * Check whether the specified {@link MethodParameter} represents a nullable Kotlin type, * an optional parameter (with a default value in the Kotlin declaration) or a * {@code Continuation} parameter used in suspending functions. */
public static boolean isOptional(MethodParameter param) { Method method = param.getMethod(); int index = param.getParameterIndex(); if (method != null && index == -1) { KFunction<?> function = ReflectJvmMapping.getKotlinFunction(method); return (function != null && function.getReturnType().isMarkedNullable()); } KFunction<?> function; Predicate<KParameter> predicate; if (method != null) { if (param.getParameterType().getName().equals("kotlin.coroutines.Continuation")) { return true; } function = ReflectJvmMapping.getKotlinFunction(method); predicate = p -> KParameter.Kind.VALUE.equals(p.getKind()); } else { Constructor<?> ctor = param.getConstructor(); Assert.state(ctor != null, "Neither method nor constructor found"); function = ReflectJvmMapping.getKotlinFunction(ctor); predicate = p -> (KParameter.Kind.VALUE.equals(p.getKind()) || KParameter.Kind.INSTANCE.equals(p.getKind())); } if (function != null) { int i = 0; for (KParameter kParameter : function.getParameters()) { if (predicate.test(kParameter)) { if (index == i++) { return (kParameter.getType().isMarkedNullable() || kParameter.isOptional()); } } } } return false; }
Return the generic return type of the method, with support of suspending functions via Kotlin reflection.
/** * Return the generic return type of the method, with support of suspending * functions via Kotlin reflection. */
private static Type getGenericReturnType(Method method) { try { KFunction<?> function = ReflectJvmMapping.getKotlinFunction(method); if (function != null && function.isSuspend()) { return ReflectJvmMapping.getJavaType(function.getReturnType()); } } catch (UnsupportedOperationException ex) { // probably a synthetic class - let's use java reflection instead } return method.getGenericReturnType(); }
Return the return type of the method, with support of suspending functions via Kotlin reflection.
/** * Return the return type of the method, with support of suspending * functions via Kotlin reflection. */
private static Class<?> getReturnType(Method method) { try { KFunction<?> function = ReflectJvmMapping.getKotlinFunction(method); if (function != null && function.isSuspend()) { Type paramType = ReflectJvmMapping.getJavaType(function.getReturnType()); if (paramType == Unit.class) { paramType = void.class; } return ResolvableType.forType(paramType).resolve(method.getReturnType()); } } catch (UnsupportedOperationException ex) { // probably a synthetic class - let's use java reflection instead } return method.getReturnType(); } } }