/*
 * Copyright 2002-2018 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
 *
 *      http://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.List;
import java.util.Map;
import java.util.Optional;
import java.util.function.Predicate;
import java.util.stream.Collectors;

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;

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
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 * @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 = 1;
Map from Integer level to Integer type index.
/** Map from Integer level to Integer type index. */
@Nullable Map<Integer, Integer> typeIndexesPerLevel; @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; }
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()
/** * Increase this parameter's nesting level. * @see #getNestingLevel() */
public void increaseNestingLevel() { this.nestingLevel++; }
Decrease this parameter's nesting level.
See Also:
  • getNestingLevel()
/** * Decrease this parameter's nesting level. * @see #getNestingLevel() */
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; }
Set the type index for the current nesting level.
Params:
  • typeIndex – the corresponding type index (or null for the default type index)
See Also:
/** * 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() */
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. This is effectively the same as increaseNestingLevel(), just with an independent MethodParameter object (e.g. in case of the original being cached).
Since:4.3
/** * Return a variant of this {@code MethodParameter} which points to the * same parameter but one nesting level deeper. This is effectively the * same as {@link #increaseNestingLevel()}, just with an independent * {@code MethodParameter} object (e.g. in case of the original being cached). * @since 4.3 */
public MethodParameter nested() { MethodParameter nestedParam = this.nestedMethodParameter; if (nestedParam != null) { return nestedParam; } nestedParam = clone(); nestedParam.nestingLevel = this.nestingLevel + 1; this.nestedMethodParameter = nestedParam; return nestedParam; }
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 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 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); }
Set a containing class to resolve the parameter type against.
/** * Set a containing class to resolve the parameter type against. */
void setContainingClass(Class<?> containingClass) { this.containingClass = containingClass; } public Class<?> getContainingClass() { Class<?> containingClass = this.containingClass; return (containingClass != null ? containingClass : getDeclaringClass()); }
Set a resolved (generic) parameter type.
/** * Set a resolved (generic) parameter type. */
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) { if (this.parameterIndex < 0) { Method method = getMethod(); paramType = (method != null ? method.getReturnType() : void.class); } else { paramType = this.executable.getParameterTypes()[this.parameterIndex]; } 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 ? 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] : getParameterType()); } this.genericParameterType = paramType; } return paramType; }
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(Object other) { if (this == other) { return true; } if (!(other instanceof MethodParameter)) { return false; } MethodParameter otherParam = (MethodParameter) other; return (this.parameterIndex == otherParam.parameterIndex && getExecutable().equals(otherParam.getExecutable())); } @Override public int hashCode() { return (getExecutable().hashCode() * 31 + 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 or an optional parameter (with a default value in the Kotlin declaration).
/** * Check whether the specified {@link MethodParameter} represents a nullable Kotlin type * or an optional parameter (with a default value in the Kotlin declaration). */
public static boolean isOptional(MethodParameter param) { Method method = param.getMethod(); Constructor<?> ctor = param.getConstructor(); int index = param.getParameterIndex(); if (method != null && index == -1) { KFunction<?> function = ReflectJvmMapping.getKotlinFunction(method); return (function != null && function.getReturnType().isMarkedNullable()); } else { KFunction<?> function = null; Predicate<KParameter> predicate = null; if (method != null) { function = ReflectJvmMapping.getKotlinFunction(method); predicate = p -> KParameter.Kind.VALUE.equals(p.getKind()); } else if (ctor != null) { function = ReflectJvmMapping.getKotlinFunction(ctor); predicate = p -> KParameter.Kind.VALUE.equals(p.getKind()) || KParameter.Kind.INSTANCE.equals(p.getKind()); } if (function != null) { List<KParameter> parameters = function.getParameters(); KParameter parameter = parameters .stream() .filter(predicate) .collect(Collectors.toList()) .get(index); return (parameter.getType().isMarkedNullable() || parameter.isOptional()); } } return false; } } }