MethodUtils.java

  1. /*
  2.  * Licensed to the Apache Software Foundation (ASF) under one or more
  3.  * contributor license agreements.  See the NOTICE file distributed with
  4.  * this work for additional information regarding copyright ownership.
  5.  * The ASF licenses this file to You under the Apache License, Version 2.0
  6.  * (the "License"); you may not use this file except in compliance with
  7.  * the License.  You may obtain a copy of the License at
  8.  *
  9.  *      https://www.apache.org/licenses/LICENSE-2.0
  10.  *
  11.  * Unless required by applicable law or agreed to in writing, software
  12.  * distributed under the License is distributed on an "AS IS" BASIS,
  13.  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14.  * See the License for the specific language governing permissions and
  15.  * limitations under the License.
  16.  */
  17. package org.apache.commons.lang3.reflect;

  18. import java.lang.annotation.Annotation;
  19. import java.lang.reflect.Array;
  20. import java.lang.reflect.InvocationTargetException;
  21. import java.lang.reflect.Method;
  22. import java.lang.reflect.Type;
  23. import java.lang.reflect.TypeVariable;
  24. import java.util.ArrayList;
  25. import java.util.Arrays;
  26. import java.util.Comparator;
  27. import java.util.Iterator;
  28. import java.util.LinkedHashSet;
  29. import java.util.List;
  30. import java.util.Map;
  31. import java.util.Objects;
  32. import java.util.Set;
  33. import java.util.TreeMap;
  34. import java.util.stream.Collectors;
  35. import java.util.stream.Stream;

  36. import org.apache.commons.lang3.ArrayUtils;
  37. import org.apache.commons.lang3.ClassUtils;
  38. import org.apache.commons.lang3.ClassUtils.Interfaces;
  39. import org.apache.commons.lang3.Validate;

  40. /**
  41.  * Utility reflection methods focused on {@link Method}s, originally from Commons BeanUtils.
  42.  * Differences from the BeanUtils version may be noted, especially where similar functionality
  43.  * already existed within Lang.
  44.  *
  45.  * <h2>Known Limitations</h2>
  46.  * <h3>Accessing Public Methods In A Default Access Superclass</h3>
  47.  * <p>There is an issue when invoking {@code public} methods contained in a default access superclass on JREs prior to 1.4.
  48.  * Reflection locates these methods fine and correctly assigns them as {@code public}.
  49.  * However, an {@link IllegalAccessException} is thrown if the method is invoked.</p>
  50.  *
  51.  * <p>{@link MethodUtils} contains a workaround for this situation.
  52.  * It will attempt to call {@link java.lang.reflect.AccessibleObject#setAccessible(boolean)} on this method.
  53.  * If this call succeeds, then the method can be invoked as normal.
  54.  * This call will only succeed when the application has sufficient security privileges.
  55.  * If this call fails then the method may fail.</p>
  56.  *
  57.  * @since 2.5
  58.  */
  59. public class MethodUtils {

  60.     private static final Comparator<Method> METHOD_BY_SIGNATURE = Comparator.comparing(Method::toString);

  61.     /**
  62.      * Returns the aggregate number of inheritance hops between assignable argument class types.  Returns -1
  63.      * if the arguments aren't assignable.  Fills a specific purpose for getMatchingMethod and is not generalized.
  64.      *
  65.      * @param fromClassArray the Class array to calculate the distance from.
  66.      * @param toClassArray the Class array to calculate the distance to.
  67.      * @return the aggregate number of inheritance hops between assignable argument class types.
  68.      */
  69.     private static int distance(final Class<?>[] fromClassArray, final Class<?>[] toClassArray) {
  70.         int answer = 0;

  71.         if (!ClassUtils.isAssignable(fromClassArray, toClassArray, true)) {
  72.             return -1;
  73.         }
  74.         for (int offset = 0; offset < fromClassArray.length; offset++) {
  75.             // Note InheritanceUtils.distance() uses different scoring system.
  76.             final Class<?> aClass = fromClassArray[offset];
  77.             final Class<?> toClass = toClassArray[offset];
  78.             if (aClass == null || aClass.equals(toClass)) {
  79.                 continue;
  80.             }
  81.             if (ClassUtils.isAssignable(aClass, toClass, true)
  82.                     && !ClassUtils.isAssignable(aClass, toClass, false)) {
  83.                 answer++;
  84.             } else {
  85.                 answer += 2;
  86.             }
  87.         }

  88.         return answer;
  89.     }

  90.     /**
  91.      * Gets an accessible method (that is, one that can be invoked via reflection) with given name and parameters. If no such method can be found, return
  92.      * {@code null}. This is just a convenience wrapper for {@link #getAccessibleMethod(Method)}.
  93.      *
  94.      * @param cls            get method from this class
  95.      * @param methodName     get method with this name
  96.      * @param parameterTypes with these parameters types
  97.      * @return The accessible method
  98.      */
  99.     public static Method getAccessibleMethod(final Class<?> cls, final String methodName, final Class<?>... parameterTypes) {
  100.         return getAccessibleMethod(getMethodObject(cls, methodName, parameterTypes));
  101.     }

  102.     /**
  103.      * Gets an accessible method (that is, one that can be invoked via
  104.      * reflection) that implements the specified Method. If no such method
  105.      * can be found, return {@code null}.
  106.      *
  107.      * @param method The method that we wish to call, may be null.
  108.      * @return The accessible method
  109.      */
  110.     public static Method getAccessibleMethod(Method method) {
  111.         if (!MemberUtils.isAccessible(method)) {
  112.             return null;
  113.         }
  114.         // If the declaring class is public, we are done
  115.         final Class<?> cls = method.getDeclaringClass();
  116.         if (ClassUtils.isPublic(cls)) {
  117.             return method;
  118.         }
  119.         final String methodName = method.getName();
  120.         final Class<?>[] parameterTypes = method.getParameterTypes();

  121.         // Check the implemented interfaces and subinterfaces
  122.         method = getAccessibleMethodFromInterfaceNest(cls, methodName,
  123.                 parameterTypes);

  124.         // Check the superclass chain
  125.         if (method == null) {
  126.             method = getAccessibleMethodFromSuperclass(cls, methodName,
  127.                     parameterTypes);
  128.         }
  129.         return method;
  130.     }

  131.     /**
  132.      * Gets an accessible method (that is, one that can be invoked via
  133.      * reflection) that implements the specified method, by scanning through
  134.      * all implemented interfaces and subinterfaces. If no such method
  135.      * can be found, return {@code null}.
  136.      *
  137.      * <p>There isn't any good reason why this method must be {@code private}.
  138.      * It is because there doesn't seem any reason why other classes should
  139.      * call this rather than the higher level methods.</p>
  140.      *
  141.      * @param cls Parent class for the interfaces to be checked
  142.      * @param methodName Method name of the method we wish to call
  143.      * @param parameterTypes The parameter type signatures
  144.      * @return the accessible method or {@code null} if not found
  145.      */
  146.     private static Method getAccessibleMethodFromInterfaceNest(Class<?> cls,
  147.             final String methodName, final Class<?>... parameterTypes) {
  148.         // Search up the superclass chain
  149.         for (; cls != null; cls = cls.getSuperclass()) {

  150.             // Check the implemented interfaces of the parent class
  151.             final Class<?>[] interfaces = cls.getInterfaces();
  152.             for (final Class<?> anInterface : interfaces) {
  153.                 // Is this interface public?
  154.                 if (!ClassUtils.isPublic(anInterface)) {
  155.                     continue;
  156.                 }
  157.                 // Does the method exist on this interface?
  158.                 try {
  159.                     return anInterface.getDeclaredMethod(methodName,
  160.                             parameterTypes);
  161.                 } catch (final NoSuchMethodException ignored) {
  162.                     /*
  163.                      * Swallow, if no method is found after the loop then this
  164.                      * method returns null.
  165.                      */
  166.                 }
  167.                 // Recursively check our parent interfaces
  168.                 final Method method = getAccessibleMethodFromInterfaceNest(anInterface,
  169.                         methodName, parameterTypes);
  170.                 if (method != null) {
  171.                     return method;
  172.                 }
  173.             }
  174.         }
  175.         return null;
  176.     }

  177.     /**
  178.      * Gets an accessible method (that is, one that can be invoked via
  179.      * reflection) by scanning through the superclasses. If no such method
  180.      * can be found, return {@code null}.
  181.      *
  182.      * @param cls Class to be checked
  183.      * @param methodName Method name of the method we wish to call
  184.      * @param parameterTypes The parameter type signatures
  185.      * @return the accessible method or {@code null} if not found
  186.      */
  187.     private static Method getAccessibleMethodFromSuperclass(final Class<?> cls,
  188.             final String methodName, final Class<?>... parameterTypes) {
  189.         Class<?> parentClass = cls.getSuperclass();
  190.         while (parentClass != null) {
  191.             if (ClassUtils.isPublic(parentClass)) {
  192.                 return getMethodObject(parentClass, methodName, parameterTypes);
  193.             }
  194.             parentClass = parentClass.getSuperclass();
  195.         }
  196.         return null;
  197.     }

  198.     /**
  199.      * Gets a combination of {@link ClassUtils#getAllSuperclasses(Class)} and
  200.      * {@link ClassUtils#getAllInterfaces(Class)}, one from superclasses, one
  201.      * from interfaces, and so on in a breadth first way.
  202.      *
  203.      * @param cls  the class to look up, may be {@code null}
  204.      * @return the combined {@link List} of superclasses and interfaces in order
  205.      * going up from this one
  206.      *  {@code null} if null input
  207.      */
  208.     private static List<Class<?>> getAllSuperclassesAndInterfaces(final Class<?> cls) {
  209.         if (cls == null) {
  210.             return null;
  211.         }

  212.         final List<Class<?>> allSuperClassesAndInterfaces = new ArrayList<>();
  213.         final List<Class<?>> allSuperclasses = ClassUtils.getAllSuperclasses(cls);
  214.         int superClassIndex = 0;
  215.         final List<Class<?>> allInterfaces = ClassUtils.getAllInterfaces(cls);
  216.         int interfaceIndex = 0;
  217.         while (interfaceIndex < allInterfaces.size() ||
  218.                 superClassIndex < allSuperclasses.size()) {
  219.             final Class<?> acls;
  220.             if (interfaceIndex >= allInterfaces.size() || superClassIndex < allSuperclasses.size() && superClassIndex < interfaceIndex) {
  221.                 acls = allSuperclasses.get(superClassIndex++);
  222.             } else {
  223.                 acls = allInterfaces.get(interfaceIndex++);
  224.             }
  225.             allSuperClassesAndInterfaces.add(acls);
  226.         }
  227.         return allSuperClassesAndInterfaces;
  228.     }

  229.     /**
  230.      * Gets the annotation object with the given annotation type that is present on the given method
  231.      * or optionally on any equivalent method in super classes and interfaces. Returns null if the annotation
  232.      * type was not present.
  233.      *
  234.      * <p>Stops searching for an annotation once the first annotation of the specified type has been
  235.      * found. Additional annotations of the specified type will be silently ignored.</p>
  236.      * @param <A>
  237.      *            the annotation type
  238.      * @param method
  239.      *            the {@link Method} to query, may be null.
  240.      * @param annotationCls
  241.      *            the {@link Annotation} to check if is present on the method
  242.      * @param searchSupers
  243.      *            determines if a lookup in the entire inheritance hierarchy of the given class is performed
  244.      *            if the annotation was not directly present
  245.      * @param ignoreAccess
  246.      *            determines if underlying method has to be accessible
  247.      * @return the first matching annotation, or {@code null} if not found
  248.      * @throws NullPointerException if either the method or annotation class is {@code null}
  249.      * @throws SecurityException if an underlying accessible object's method denies the request.
  250.      * @see SecurityManager#checkPermission
  251.      * @since 3.6
  252.      */
  253.     public static <A extends Annotation> A getAnnotation(final Method method, final Class<A> annotationCls,
  254.                                                          final boolean searchSupers, final boolean ignoreAccess) {
  255.         Objects.requireNonNull(method, "method");
  256.         Objects.requireNonNull(annotationCls, "annotationCls");
  257.         if (!ignoreAccess && !MemberUtils.isAccessible(method)) {
  258.             return null;
  259.         }
  260.         A annotation = method.getAnnotation(annotationCls);
  261.         if (annotation == null && searchSupers) {
  262.             final Class<?> mcls = method.getDeclaringClass();
  263.             final List<Class<?>> classes = getAllSuperclassesAndInterfaces(mcls);
  264.             for (final Class<?> acls : classes) {
  265.                 final Method equivalentMethod = ignoreAccess ? getMatchingMethod(acls, method.getName(), method.getParameterTypes())
  266.                         : getMatchingAccessibleMethod(acls, method.getName(), method.getParameterTypes());
  267.                 if (equivalentMethod != null) {
  268.                     annotation = equivalentMethod.getAnnotation(annotationCls);
  269.                     if (annotation != null) {
  270.                         break;
  271.                     }
  272.                 }
  273.             }
  274.         }
  275.         return annotation;
  276.     }

  277.     /**
  278.      * Gets an accessible method that matches the given name and has compatible parameters.
  279.      * Compatible parameters mean that every method parameter is assignable from
  280.      * the given parameters.
  281.      * In other words, it finds a method with the given name
  282.      * that will take the parameters given.
  283.      *
  284.      * <p>This method is used by
  285.      * {@link
  286.      * #invokeMethod(Object object, String methodName, Object[] args, Class[] parameterTypes)}.
  287.      * </p>
  288.      *
  289.      * <p>This method can match primitive parameter by passing in wrapper classes.
  290.      * For example, a {@link Boolean} will match a primitive {@code boolean}
  291.      * parameter.
  292.      * </p>
  293.      *
  294.      * @param cls find method in this class
  295.      * @param methodName find method with this name
  296.      * @param parameterTypes find method with most compatible parameters
  297.      * @return The accessible method
  298.      * @throws SecurityException if an underlying accessible object's method denies the request.
  299.      * @see SecurityManager#checkPermission
  300.      */
  301.     public static Method getMatchingAccessibleMethod(final Class<?> cls,
  302.         final String methodName, final Class<?>... parameterTypes) {
  303.         final Method candidate = getMethodObject(cls, methodName, parameterTypes);
  304.         if (candidate != null) {
  305.             return MemberUtils.setAccessibleWorkaround(candidate);
  306.         }
  307.         // search through all methods
  308.         final Method[] methods = cls.getMethods();
  309.         final List<Method> matchingMethods = Stream.of(methods)
  310.             .filter(method -> method.getName().equals(methodName) && MemberUtils.isMatchingMethod(method, parameterTypes)).collect(Collectors.toList());

  311.         // Sort methods by signature to force deterministic result
  312.         matchingMethods.sort(METHOD_BY_SIGNATURE);

  313.         Method bestMatch = null;
  314.         for (final Method method : matchingMethods) {
  315.             // get accessible version of method
  316.             final Method accessibleMethod = getAccessibleMethod(method);
  317.             if (accessibleMethod != null && (bestMatch == null || MemberUtils.compareMethodFit(accessibleMethod, bestMatch, parameterTypes) < 0)) {
  318.                 bestMatch = accessibleMethod;
  319.             }
  320.         }
  321.         if (bestMatch != null) {
  322.             MemberUtils.setAccessibleWorkaround(bestMatch);
  323.         }

  324.         if (bestMatch != null && bestMatch.isVarArgs() && bestMatch.getParameterTypes().length > 0 && parameterTypes.length > 0) {
  325.             final Class<?>[] methodParameterTypes = bestMatch.getParameterTypes();
  326.             final Class<?> methodParameterComponentType = methodParameterTypes[methodParameterTypes.length - 1].getComponentType();
  327.             final String methodParameterComponentTypeName = ClassUtils.primitiveToWrapper(methodParameterComponentType).getName();

  328.             final Class<?> lastParameterType = parameterTypes[parameterTypes.length - 1];
  329.             final String parameterTypeName = lastParameterType == null ? null : lastParameterType.getName();
  330.             final String parameterTypeSuperClassName = lastParameterType == null ? null
  331.                     : lastParameterType.getSuperclass() != null ? lastParameterType.getSuperclass().getName() : null;

  332.             if (parameterTypeName != null && parameterTypeSuperClassName != null && !methodParameterComponentTypeName.equals(parameterTypeName)
  333.                 && !methodParameterComponentTypeName.equals(parameterTypeSuperClassName)) {
  334.                 return null;
  335.             }
  336.         }

  337.         return bestMatch;
  338.     }

  339.     /**
  340.      * Gets a method whether or not it's accessible. If no such method
  341.      * can be found, return {@code null}.
  342.      *
  343.      * @param cls The class that will be subjected to the method search
  344.      * @param methodName The method that we wish to call
  345.      * @param parameterTypes Argument class types
  346.      * @throws IllegalStateException if there is no unique result
  347.      * @throws NullPointerException if the class is {@code null}
  348.      * @return The method
  349.      * @since 3.5
  350.      */
  351.     public static Method getMatchingMethod(final Class<?> cls, final String methodName,
  352.             final Class<?>... parameterTypes) {
  353.         Objects.requireNonNull(cls, "cls");
  354.         Validate.notEmpty(methodName, "methodName");

  355.         final List<Method> methods = Stream.of(cls.getDeclaredMethods())
  356.                 .filter(method -> method.getName().equals(methodName))
  357.                 .collect(Collectors.toList());

  358.         getAllSuperclassesAndInterfaces(cls).stream()
  359.                 .map(Class::getDeclaredMethods)
  360.                 .flatMap(Stream::of)
  361.                 .filter(method -> method.getName().equals(methodName))
  362.                 .forEach(methods::add);

  363.         for (final Method method : methods) {
  364.             if (Arrays.deepEquals(method.getParameterTypes(), parameterTypes)) {
  365.                 return method;
  366.             }
  367.         }

  368.         final TreeMap<Integer, List<Method>> candidates = new TreeMap<>();

  369.         methods.stream()
  370.                 .filter(method -> ClassUtils.isAssignable(parameterTypes, method.getParameterTypes(), true))
  371.                 .forEach(method -> {
  372.                     final int distance = distance(parameterTypes, method.getParameterTypes());
  373.                     final List<Method> candidatesAtDistance = candidates.computeIfAbsent(distance, k -> new ArrayList<>());
  374.                     candidatesAtDistance.add(method);
  375.                 });

  376.         if (candidates.isEmpty()) {
  377.             return null;
  378.         }

  379.         final List<Method> bestCandidates = candidates.values().iterator().next();
  380.         if (bestCandidates.size() == 1 || !Objects.equals(bestCandidates.get(0).getDeclaringClass(),
  381.                 bestCandidates.get(1).getDeclaringClass())) {
  382.             return bestCandidates.get(0);
  383.         }

  384.         throw new IllegalStateException(
  385.                 String.format("Found multiple candidates for method %s on class %s : %s",
  386.                         methodName + Stream.of(parameterTypes).map(String::valueOf).collect(Collectors.joining(",", "(", ")")),
  387.                         cls.getName(),
  388.                         bestCandidates.stream().map(Method::toString).collect(Collectors.joining(",", "[", "]")))
  389.         );
  390.     }

  391.     /**
  392.      * Gets a Method or null if a {@link Class#getMethod(String, Class...) documented} exception is thrown.
  393.      *
  394.      * @param cls Receiver for {@link Class#getMethod(String, Class...)}.
  395.      * @param name the name of the method
  396.      * @param parameterTypes the list of parameters
  397.      * @return a Method or null.
  398.      * @throws SecurityException if an underlying accessible object's method denies the request.
  399.      * @see SecurityManager#checkPermission
  400.      * @see Class#getMethod(String, Class...)
  401.      * @since 3.15.0
  402.      */
  403.     public static Method getMethodObject(final Class<?> cls, final String name, final Class<?>... parameterTypes) {
  404.         try {
  405.             return cls.getMethod(name, parameterTypes);
  406.         } catch (final NoSuchMethodException | SecurityException e) {
  407.             return null;
  408.         }
  409.     }

  410.     /**
  411.      * Gets all class level public methods of the given class that are annotated with the given annotation.
  412.      * @param cls
  413.      *            the {@link Class} to query
  414.      * @param annotationCls
  415.      *            the {@link Annotation} that must be present on a method to be matched
  416.      * @return a list of Methods (possibly empty).
  417.      * @throws NullPointerException
  418.      *            if the class or annotation are {@code null}
  419.      * @since 3.4
  420.      */
  421.     public static List<Method> getMethodsListWithAnnotation(final Class<?> cls, final Class<? extends Annotation> annotationCls) {
  422.         return getMethodsListWithAnnotation(cls, annotationCls, false, false);
  423.     }

  424.     /**
  425.      * Gets all methods of the given class that are annotated with the given annotation.
  426.      *
  427.      * @param cls
  428.      *            the {@link Class} to query
  429.      * @param annotationCls
  430.      *            the {@link Annotation} that must be present on a method to be matched
  431.      * @param searchSupers
  432.      *            determines if a lookup in the entire inheritance hierarchy of the given class should be performed
  433.      * @param ignoreAccess
  434.      *            determines if non-public methods should be considered
  435.      * @return a list of Methods (possibly empty).
  436.      * @throws NullPointerException if either the class or annotation class is {@code null}
  437.      * @since 3.6
  438.      */
  439.     public static List<Method> getMethodsListWithAnnotation(final Class<?> cls,
  440.                                                             final Class<? extends Annotation> annotationCls,
  441.                                                             final boolean searchSupers, final boolean ignoreAccess) {

  442.         Objects.requireNonNull(cls, "cls");
  443.         Objects.requireNonNull(annotationCls, "annotationCls");
  444.         final List<Class<?>> classes = searchSupers ? getAllSuperclassesAndInterfaces(cls) : new ArrayList<>();
  445.         classes.add(0, cls);
  446.         final List<Method> annotatedMethods = new ArrayList<>();
  447.         classes.forEach(acls -> {
  448.             final Method[] methods = ignoreAccess ? acls.getDeclaredMethods() : acls.getMethods();
  449.             Stream.of(methods).filter(method -> method.isAnnotationPresent(annotationCls)).forEachOrdered(annotatedMethods::add);
  450.         });
  451.         return annotatedMethods;
  452.     }

  453.     /**
  454.      * Gets all class level public methods of the given class that are annotated with the given annotation.
  455.      *
  456.      * @param cls
  457.      *            the {@link Class} to query
  458.      * @param annotationCls
  459.      *            the {@link java.lang.annotation.Annotation} that must be present on a method to be matched
  460.      * @return an array of Methods (possibly empty).
  461.      * @throws NullPointerException if the class or annotation are {@code null}
  462.      * @since 3.4
  463.      */
  464.     public static Method[] getMethodsWithAnnotation(final Class<?> cls, final Class<? extends Annotation> annotationCls) {
  465.         return getMethodsWithAnnotation(cls, annotationCls, false, false);
  466.     }

  467.     /**
  468.      * Gets all methods of the given class that are annotated with the given annotation.
  469.      *
  470.      * @param cls
  471.      *            the {@link Class} to query
  472.      * @param annotationCls
  473.      *            the {@link java.lang.annotation.Annotation} that must be present on a method to be matched
  474.      * @param searchSupers
  475.      *            determines if a lookup in the entire inheritance hierarchy of the given class should be performed
  476.      * @param ignoreAccess
  477.      *            determines if non-public methods should be considered
  478.      * @return an array of Methods (possibly empty).
  479.      * @throws NullPointerException if the class or annotation are {@code null}
  480.      * @since 3.6
  481.      */
  482.     public static Method[] getMethodsWithAnnotation(final Class<?> cls, final Class<? extends Annotation> annotationCls,
  483.         final boolean searchSupers, final boolean ignoreAccess) {
  484.         return getMethodsListWithAnnotation(cls, annotationCls, searchSupers, ignoreAccess).toArray(ArrayUtils.EMPTY_METHOD_ARRAY);
  485.     }

  486.     /**
  487.      * Gets the hierarchy of overridden methods down to {@code result} respecting generics.
  488.      *
  489.      * @param method lowest to consider
  490.      * @param interfacesBehavior whether to search interfaces, {@code null} {@code implies} false
  491.      * @return a {@code Set<Method>} in ascending order from sub- to superclass
  492.      * @throws NullPointerException if the specified method is {@code null}
  493.      * @throws SecurityException if an underlying accessible object's method denies the request.
  494.      * @see SecurityManager#checkPermission
  495.      * @since 3.2
  496.      */
  497.     public static Set<Method> getOverrideHierarchy(final Method method, final Interfaces interfacesBehavior) {
  498.         Objects.requireNonNull(method, "method");
  499.         final Set<Method> result = new LinkedHashSet<>();
  500.         result.add(method);

  501.         final Class<?>[] parameterTypes = method.getParameterTypes();

  502.         final Class<?> declaringClass = method.getDeclaringClass();

  503.         final Iterator<Class<?>> hierarchy = ClassUtils.hierarchy(declaringClass, interfacesBehavior).iterator();
  504.         //skip the declaring class :P
  505.         hierarchy.next();
  506.         hierarchyTraversal: while (hierarchy.hasNext()) {
  507.             final Class<?> c = hierarchy.next();
  508.             final Method m = getMatchingAccessibleMethod(c, method.getName(), parameterTypes);
  509.             if (m == null) {
  510.                 continue;
  511.             }
  512.             if (Arrays.equals(m.getParameterTypes(), parameterTypes)) {
  513.                 // matches without generics
  514.                 result.add(m);
  515.                 continue;
  516.             }
  517.             // necessary to get arguments every time in the case that we are including interfaces
  518.             final Map<TypeVariable<?>, Type> typeArguments = TypeUtils.getTypeArguments(declaringClass, m.getDeclaringClass());
  519.             for (int i = 0; i < parameterTypes.length; i++) {
  520.                 final Type childType = TypeUtils.unrollVariables(typeArguments, method.getGenericParameterTypes()[i]);
  521.                 final Type parentType = TypeUtils.unrollVariables(typeArguments, m.getGenericParameterTypes()[i]);
  522.                 if (!TypeUtils.equals(childType, parentType)) {
  523.                     continue hierarchyTraversal;
  524.                 }
  525.             }
  526.             result.add(m);
  527.         }
  528.         return result;
  529.     }

  530.     /**
  531.      * Gets an array of arguments in the canonical form, given an arguments array passed to a varargs method,
  532.      * for example an array with the declared number of parameters, and whose last parameter is an array of the varargs type.
  533.      *
  534.      * @param args the array of arguments passed to the varags method
  535.      * @param methodParameterTypes the declared array of method parameter types
  536.      * @return an array of the variadic arguments passed to the method
  537.      * @since 3.5
  538.      */
  539.     static Object[] getVarArgs(final Object[] args, final Class<?>[] methodParameterTypes) {
  540.         if (args.length == methodParameterTypes.length
  541.                 && (args[args.length - 1] == null || args[args.length - 1].getClass().equals(methodParameterTypes[methodParameterTypes.length - 1]))) {
  542.             // The args array is already in the canonical form for the method.
  543.             return args;
  544.         }

  545.         // Construct a new array matching the method's declared parameter types.
  546.         // Copy the normal (non-varargs) parameters
  547.         final Object[] newArgs = ArrayUtils.arraycopy(args, 0, 0, methodParameterTypes.length - 1, () -> new Object[methodParameterTypes.length]);

  548.         // Construct a new array for the variadic parameters
  549.         final Class<?> varArgComponentType = methodParameterTypes[methodParameterTypes.length - 1].getComponentType();
  550.         final int varArgLength = args.length - methodParameterTypes.length + 1;

  551.         // Copy the variadic arguments into the varargs array.
  552.         Object varArgsArray = ArrayUtils.arraycopy(args, methodParameterTypes.length - 1, 0, varArgLength,
  553.                 s -> Array.newInstance(ClassUtils.primitiveToWrapper(varArgComponentType), varArgLength));

  554.         if (varArgComponentType.isPrimitive()) {
  555.             // unbox from wrapper type to primitive type
  556.             varArgsArray = ArrayUtils.toPrimitive(varArgsArray);
  557.         }

  558.         // Store the varargs array in the last position of the array to return
  559.         newArgs[methodParameterTypes.length - 1] = varArgsArray;

  560.         // Return the canonical varargs array.
  561.         return newArgs;
  562.     }

  563.     /**
  564.      * Invokes a method whose parameter types match exactly the object
  565.      * types.
  566.      *
  567.      * <p>This uses reflection to invoke the method obtained from a call to
  568.      * {@link #getAccessibleMethod}(Class, String, Class[])}.</p>
  569.      *
  570.      * @param object invoke method on this object
  571.      * @param methodName get method with this name
  572.      * @return The value returned by the invoked method
  573.      * @throws NoSuchMethodException if there is no such accessible method
  574.      * @throws InvocationTargetException wraps an exception thrown by the
  575.      *  method invoked
  576.      * @throws IllegalAccessException if the requested method is not accessible
  577.      *  via reflection
  578.      *
  579.      * @since 3.4
  580.      */
  581.     public static Object invokeExactMethod(final Object object, final String methodName) throws NoSuchMethodException,
  582.             IllegalAccessException, InvocationTargetException {
  583.         return invokeExactMethod(object, methodName, ArrayUtils.EMPTY_OBJECT_ARRAY, null);
  584.     }

  585.     /**
  586.      * Invokes a method with no parameters.
  587.      *
  588.      * <p>This uses reflection to invoke the method obtained from a call to
  589.      * {@link #getAccessibleMethod}(Class, String, Class[])}.</p>
  590.      *
  591.      * @param object invoke method on this object
  592.      * @param methodName get method with this name
  593.      * @param args use these arguments - treat null as empty array
  594.      * @return The value returned by the invoked method
  595.      * @throws NoSuchMethodException if there is no such accessible method
  596.      * @throws InvocationTargetException wraps an exception thrown by the
  597.      *  method invoked
  598.      * @throws IllegalAccessException if the requested method is not accessible
  599.      *  via reflection
  600.      * @throws NullPointerException if the object or method name are {@code null}
  601.      */
  602.     public static Object invokeExactMethod(final Object object, final String methodName,
  603.             Object... args) throws NoSuchMethodException,
  604.             IllegalAccessException, InvocationTargetException {
  605.         args = ArrayUtils.nullToEmpty(args);
  606.         return invokeExactMethod(object, methodName, args, ClassUtils.toClass(args));
  607.     }

  608.     /**
  609.      * Invokes a method whose parameter types match exactly the parameter
  610.      * types given.
  611.      *
  612.      * <p>This uses reflection to invoke the method obtained from a call to
  613.      * {@link #getAccessibleMethod(Class, String, Class[])}.</p>
  614.      *
  615.      * @param object invoke method on this object
  616.      * @param methodName get method with this name
  617.      * @param args use these arguments - treat null as empty array
  618.      * @param parameterTypes match these parameters - treat {@code null} as empty array
  619.      * @return The value returned by the invoked method
  620.      * @throws NoSuchMethodException if there is no such accessible method
  621.      * @throws InvocationTargetException wraps an exception thrown by the
  622.      *  method invoked
  623.      * @throws IllegalAccessException if the requested method is not accessible
  624.      *  via reflection
  625.      * @throws NullPointerException if the object or method name are {@code null}
  626.      */
  627.     public static Object invokeExactMethod(final Object object, final String methodName, Object[] args, Class<?>[] parameterTypes)
  628.         throws NoSuchMethodException, IllegalAccessException, InvocationTargetException {
  629.         Objects.requireNonNull(object, "object");
  630.         args = ArrayUtils.nullToEmpty(args);
  631.         parameterTypes = ArrayUtils.nullToEmpty(parameterTypes);
  632.         final Class<?> cls = object.getClass();
  633.         final Method method = getAccessibleMethod(cls, methodName, parameterTypes);
  634.         if (method == null) {
  635.             throw new NoSuchMethodException("No such accessible method: " + methodName + "() on object: " + cls.getName());
  636.         }
  637.         return method.invoke(object, args);
  638.     }

  639.     /**
  640.      * Invokes a {@code static} method whose parameter types match exactly the object
  641.      * types.
  642.      *
  643.      * <p>This uses reflection to invoke the method obtained from a call to
  644.      * {@link #getAccessibleMethod(Class, String, Class[])}.</p>
  645.      *
  646.      * @param cls invoke static method on this class
  647.      * @param methodName get method with this name
  648.      * @param args use these arguments - treat {@code null} as empty array
  649.      * @return The value returned by the invoked method
  650.      * @throws NoSuchMethodException if there is no such accessible method
  651.      * @throws InvocationTargetException wraps an exception thrown by the
  652.      *  method invoked
  653.      * @throws IllegalAccessException if the requested method is not accessible
  654.      *  via reflection
  655.      */
  656.     public static Object invokeExactStaticMethod(final Class<?> cls, final String methodName,
  657.             Object... args) throws NoSuchMethodException,
  658.             IllegalAccessException, InvocationTargetException {
  659.         args = ArrayUtils.nullToEmpty(args);
  660.         return invokeExactStaticMethod(cls, methodName, args, ClassUtils.toClass(args));
  661.     }

  662.     /**
  663.      * Invokes a {@code static} method whose parameter types match exactly the parameter
  664.      * types given.
  665.      *
  666.      * <p>This uses reflection to invoke the method obtained from a call to
  667.      * {@link #getAccessibleMethod(Class, String, Class[])}.</p>
  668.      *
  669.      * @param cls invoke static method on this class
  670.      * @param methodName get method with this name
  671.      * @param args use these arguments - treat {@code null} as empty array
  672.      * @param parameterTypes match these parameters - treat {@code null} as empty array
  673.      * @return The value returned by the invoked method
  674.      * @throws NoSuchMethodException if there is no such accessible method
  675.      * @throws InvocationTargetException wraps an exception thrown by the
  676.      *  method invoked
  677.      * @throws IllegalAccessException if the requested method is not accessible
  678.      *  via reflection
  679.      */
  680.     public static Object invokeExactStaticMethod(final Class<?> cls, final String methodName,
  681.             Object[] args, Class<?>[] parameterTypes)
  682.             throws NoSuchMethodException, IllegalAccessException,
  683.             InvocationTargetException {
  684.         args = ArrayUtils.nullToEmpty(args);
  685.         parameterTypes = ArrayUtils.nullToEmpty(parameterTypes);
  686.         final Method method = getAccessibleMethod(cls, methodName, parameterTypes);
  687.         if (method == null) {
  688.             throw new NoSuchMethodException("No such accessible method: "
  689.                     + methodName + "() on class: " + cls.getName());
  690.         }
  691.         return method.invoke(null, args);
  692.     }

  693.     /**
  694.      * Invokes a named method without parameters.
  695.      *
  696.      * <p>This is a convenient wrapper for
  697.      * {@link #invokeMethod(Object object, boolean forceAccess, String methodName, Object[] args, Class[] parameterTypes)}.
  698.      * </p>
  699.      *
  700.      * @param object invoke method on this object
  701.      * @param forceAccess force access to invoke method even if it's not accessible
  702.      * @param methodName get method with this name
  703.      * @return The value returned by the invoked method
  704.      * @throws NoSuchMethodException if there is no such accessible method
  705.      * @throws InvocationTargetException wraps an exception thrown by the method invoked
  706.      * @throws IllegalAccessException if the requested method is not accessible via reflection
  707.      * @throws SecurityException if an underlying accessible object's method denies the request.
  708.      * @see SecurityManager#checkPermission
  709.      * @since 3.5
  710.      */
  711.     public static Object invokeMethod(final Object object, final boolean forceAccess, final String methodName)
  712.             throws NoSuchMethodException, IllegalAccessException, InvocationTargetException {
  713.         return invokeMethod(object, forceAccess, methodName, ArrayUtils.EMPTY_OBJECT_ARRAY, null);
  714.     }

  715.     /**
  716.      * Invokes a named method whose parameter type matches the object type.
  717.      *
  718.      * <p>This method supports calls to methods taking primitive parameters
  719.      * via passing in wrapping classes. So, for example, a {@link Boolean} object
  720.      * would match a {@code boolean} primitive.</p>
  721.      *
  722.      * <p>This is a convenient wrapper for
  723.      * {@link #invokeMethod(Object object, boolean forceAccess, String methodName, Object[] args, Class[] parameterTypes)}.
  724.      * </p>
  725.      *
  726.      * @param object invoke method on this object
  727.      * @param forceAccess force access to invoke method even if it's not accessible
  728.      * @param methodName get method with this name
  729.      * @param args use these arguments - treat null as empty array
  730.      * @return The value returned by the invoked method
  731.      * @throws NoSuchMethodException if there is no such accessible method
  732.      * @throws InvocationTargetException wraps an exception thrown by the method invoked
  733.      * @throws IllegalAccessException if the requested method is not accessible via reflection
  734.      * @throws NullPointerException if the object or method name are {@code null}
  735.      * @throws SecurityException if an underlying accessible object's method denies the request.
  736.      * @see SecurityManager#checkPermission
  737.      * @since 3.5
  738.      */
  739.     public static Object invokeMethod(final Object object, final boolean forceAccess, final String methodName,
  740.             Object... args) throws NoSuchMethodException,
  741.             IllegalAccessException, InvocationTargetException {
  742.         args = ArrayUtils.nullToEmpty(args);
  743.         return invokeMethod(object, forceAccess, methodName, args, ClassUtils.toClass(args));
  744.     }

  745.     /**
  746.      * Invokes a named method whose parameter type matches the object type.
  747.      *
  748.      * <p>This method supports calls to methods taking primitive parameters
  749.      * via passing in wrapping classes. So, for example, a {@link Boolean} object
  750.      * would match a {@code boolean} primitive.</p>
  751.      *
  752.      * @param object invoke method on this object
  753.      * @param forceAccess force access to invoke method even if it's not accessible
  754.      * @param methodName get method with this name
  755.      * @param args use these arguments - treat null as empty array
  756.      * @param parameterTypes match these parameters - treat null as empty array
  757.      * @return The value returned by the invoked method
  758.      * @throws NoSuchMethodException if there is no such accessible method
  759.      * @throws InvocationTargetException wraps an exception thrown by the method invoked
  760.      * @throws IllegalAccessException if the requested method is not accessible via reflection
  761.      * @throws NullPointerException if the object or method name are {@code null}
  762.      * @throws SecurityException if an underlying accessible object's method denies the request.
  763.      * @see SecurityManager#checkPermission
  764.      * @since 3.5
  765.      */
  766.     public static Object invokeMethod(final Object object, final boolean forceAccess, final String methodName, Object[] args, Class<?>[] parameterTypes)
  767.         throws NoSuchMethodException, IllegalAccessException, InvocationTargetException {
  768.         Objects.requireNonNull(object, "object");
  769.         parameterTypes = ArrayUtils.nullToEmpty(parameterTypes);
  770.         args = ArrayUtils.nullToEmpty(args);

  771.         final String messagePrefix;
  772.         final Method method;

  773.         final Class<? extends Object> cls = object.getClass();
  774.         if (forceAccess) {
  775.             messagePrefix = "No such method: ";
  776.             method = getMatchingMethod(cls, methodName, parameterTypes);
  777.             if (method != null && !method.isAccessible()) {
  778.                 method.setAccessible(true);
  779.             }
  780.         } else {
  781.             messagePrefix = "No such accessible method: ";
  782.             method = getMatchingAccessibleMethod(cls, methodName, parameterTypes);
  783.         }

  784.         if (method == null) {
  785.             throw new NoSuchMethodException(messagePrefix + methodName + "() on object: " + cls.getName());
  786.         }
  787.         args = toVarArgs(method, args);

  788.         return method.invoke(object, args);
  789.     }

  790.     /**
  791.      * Invokes a named method without parameters.
  792.      *
  793.      * <p>This method delegates the method search to {@link #getMatchingAccessibleMethod(Class, String, Class[])}.</p>
  794.      *
  795.      * <p>This is a convenient wrapper for
  796.      * {@link #invokeMethod(Object object, String methodName, Object[] args, Class[] parameterTypes)}.
  797.      * </p>
  798.      *
  799.      * @param object invoke method on this object
  800.      * @param methodName get method with this name
  801.      * @return The value returned by the invoked method
  802.      * @throws NoSuchMethodException if there is no such accessible method
  803.      * @throws InvocationTargetException wraps an exception thrown by the method invoked
  804.      * @throws IllegalAccessException if the requested method is not accessible via reflection
  805.      * @throws SecurityException if an underlying accessible object's method denies the request.
  806.      * @see SecurityManager#checkPermission
  807.      * @since 3.4
  808.      */
  809.     public static Object invokeMethod(final Object object, final String methodName) throws NoSuchMethodException,
  810.             IllegalAccessException, InvocationTargetException {
  811.         return invokeMethod(object, methodName, ArrayUtils.EMPTY_OBJECT_ARRAY, null);
  812.     }

  813.     /**
  814.      * Invokes a named method whose parameter type matches the object type.
  815.      *
  816.      * <p>This method delegates the method search to {@link #getMatchingAccessibleMethod(Class, String, Class[])}.</p>
  817.      *
  818.      * <p>This method supports calls to methods taking primitive parameters
  819.      * via passing in wrapping classes. So, for example, a {@link Boolean} object
  820.      * would match a {@code boolean} primitive.</p>
  821.      *
  822.      * <p>This is a convenient wrapper for
  823.      * {@link #invokeMethod(Object object, String methodName, Object[] args, Class[] parameterTypes)}.
  824.      * </p>
  825.      *
  826.      * @param object invoke method on this object
  827.      * @param methodName get method with this name
  828.      * @param args use these arguments - treat null as empty array
  829.      * @return The value returned by the invoked method
  830.      * @throws NoSuchMethodException if there is no such accessible method
  831.      * @throws InvocationTargetException wraps an exception thrown by the method invoked
  832.      * @throws IllegalAccessException if the requested method is not accessible via reflection
  833.      * @throws NullPointerException if the object or method name are {@code null}
  834.      * @throws SecurityException if an underlying accessible object's method denies the request.
  835.      * @see SecurityManager#checkPermission
  836.      */
  837.     public static Object invokeMethod(final Object object, final String methodName,
  838.             Object... args) throws NoSuchMethodException,
  839.             IllegalAccessException, InvocationTargetException {
  840.         args = ArrayUtils.nullToEmpty(args);
  841.         return invokeMethod(object, methodName, args, ClassUtils.toClass(args));
  842.     }

  843.     /**
  844.      * Invokes a named method whose parameter type matches the object type.
  845.      *
  846.      * <p>This method delegates the method search to {@link #getMatchingAccessibleMethod(Class, String, Class[])}.</p>
  847.      *
  848.      * <p>This method supports calls to methods taking primitive parameters
  849.      * via passing in wrapping classes. So, for example, a {@link Boolean} object
  850.      * would match a {@code boolean} primitive.</p>
  851.      *
  852.      * @param object invoke method on this object
  853.      * @param methodName get method with this name
  854.      * @param args use these arguments - treat null as empty array
  855.      * @param parameterTypes match these parameters - treat null as empty array
  856.      * @return The value returned by the invoked method
  857.      * @throws NoSuchMethodException if there is no such accessible method
  858.      * @throws InvocationTargetException wraps an exception thrown by the method invoked
  859.      * @throws IllegalAccessException if the requested method is not accessible via reflection
  860.      * @throws SecurityException if an underlying accessible object's method denies the request.
  861.      * @see SecurityManager#checkPermission
  862.      */
  863.     public static Object invokeMethod(final Object object, final String methodName,
  864.             final Object[] args, final Class<?>[] parameterTypes)
  865.             throws NoSuchMethodException, IllegalAccessException,
  866.             InvocationTargetException {
  867.         return invokeMethod(object, false, methodName, args, parameterTypes);
  868.     }

  869.     /**
  870.      * Invokes a named {@code static} method whose parameter type matches the object type.
  871.      *
  872.      * <p>This method delegates the method search to {@link #getMatchingAccessibleMethod(Class, String, Class[])}.</p>
  873.      *
  874.      * <p>This method supports calls to methods taking primitive parameters
  875.      * via passing in wrapping classes. So, for example, a {@link Boolean} class
  876.      * would match a {@code boolean} primitive.</p>
  877.      *
  878.      * <p>This is a convenient wrapper for
  879.      * {@link #invokeStaticMethod(Class, String, Object[], Class[])}.
  880.      * </p>
  881.      *
  882.      * @param cls invoke static method on this class
  883.      * @param methodName get method with this name
  884.      * @param args use these arguments - treat {@code null} as empty array
  885.      * @return The value returned by the invoked method
  886.      * @throws NoSuchMethodException if there is no such accessible method
  887.      * @throws InvocationTargetException wraps an exception thrown by the method invoked
  888.      * @throws IllegalAccessException if the requested method is not accessible via reflection
  889.      * @throws SecurityException if an underlying accessible object's method denies the request.
  890.      * @see SecurityManager#checkPermission
  891.      */
  892.     public static Object invokeStaticMethod(final Class<?> cls, final String methodName,
  893.             Object... args) throws NoSuchMethodException,
  894.             IllegalAccessException, InvocationTargetException {
  895.         args = ArrayUtils.nullToEmpty(args);
  896.         return invokeStaticMethod(cls, methodName, args, ClassUtils.toClass(args));
  897.     }

  898.     /**
  899.      * Invokes a named {@code static} method whose parameter type matches the object type.
  900.      *
  901.      * <p>This method delegates the method search to {@link #getMatchingAccessibleMethod(Class, String, Class[])}.</p>
  902.      *
  903.      * <p>This method supports calls to methods taking primitive parameters
  904.      * via passing in wrapping classes. So, for example, a {@link Boolean} class
  905.      * would match a {@code boolean} primitive.</p>
  906.      *
  907.      * @param cls invoke static method on this class
  908.      * @param methodName get method with this name
  909.      * @param args use these arguments - treat {@code null} as empty array
  910.      * @param parameterTypes match these parameters - treat {@code null} as empty array
  911.      * @return The value returned by the invoked method
  912.      * @throws NoSuchMethodException if there is no such accessible method
  913.      * @throws InvocationTargetException wraps an exception thrown by the method invoked
  914.      * @throws IllegalAccessException if the requested method is not accessible via reflection
  915.      * @throws SecurityException if an underlying accessible object's method denies the request.
  916.      * @see SecurityManager#checkPermission
  917.      */
  918.     public static Object invokeStaticMethod(final Class<?> cls, final String methodName,
  919.             Object[] args, Class<?>[] parameterTypes)
  920.             throws NoSuchMethodException, IllegalAccessException,
  921.             InvocationTargetException {
  922.         args = ArrayUtils.nullToEmpty(args);
  923.         parameterTypes = ArrayUtils.nullToEmpty(parameterTypes);
  924.         final Method method = getMatchingAccessibleMethod(cls, methodName,
  925.                 parameterTypes);
  926.         if (method == null) {
  927.             throw new NoSuchMethodException("No such accessible method: "
  928.                     + methodName + "() on class: " + cls.getName());
  929.         }
  930.         args = toVarArgs(method, args);
  931.         return method.invoke(null, args);
  932.     }

  933.     private static Object[] toVarArgs(final Method method, Object[] args) {
  934.         if (method.isVarArgs()) {
  935.             final Class<?>[] methodParameterTypes = method.getParameterTypes();
  936.             args = getVarArgs(args, methodParameterTypes);
  937.         }
  938.         return args;
  939.     }

  940.     /**
  941.      * {@link MethodUtils} instances should NOT be constructed in standard programming.
  942.      * Instead, the class should be used as
  943.      * {@code MethodUtils.getAccessibleMethod(method)}.
  944.      *
  945.      * <p>This constructor is {@code public} to permit tools that require a JavaBean
  946.      * instance to operate.</p>
  947.      *
  948.      * @deprecated TODO Make private in 4.0.
  949.      */
  950.     @Deprecated
  951.     public MethodUtils() {
  952.         // empty
  953.     }
  954. }