View Javadoc
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  
19  import java.lang.annotation.Annotation;
20  import java.lang.reflect.Array;
21  import java.lang.reflect.Executable;
22  import java.lang.reflect.InvocationTargetException;
23  import java.lang.reflect.Method;
24  import java.lang.reflect.Type;
25  import java.lang.reflect.TypeVariable;
26  import java.util.ArrayList;
27  import java.util.Arrays;
28  import java.util.Collections;
29  import java.util.Comparator;
30  import java.util.Iterator;
31  import java.util.LinkedHashSet;
32  import java.util.List;
33  import java.util.Map;
34  import java.util.Objects;
35  import java.util.Set;
36  import java.util.TreeMap;
37  import java.util.stream.Collectors;
38  import java.util.stream.Stream;
39  
40  import org.apache.commons.lang3.ArrayUtils;
41  import org.apache.commons.lang3.ClassUtils;
42  import org.apache.commons.lang3.ClassUtils.Interfaces;
43  import org.apache.commons.lang3.Validate;
44  import org.apache.commons.lang3.stream.LangCollectors;
45  import org.apache.commons.lang3.stream.Streams;
46  
47  /**
48   * Utility reflection methods focused on {@link Method}s, originally from Commons BeanUtils.
49   * Differences from the BeanUtils version may be noted, especially where similar functionality
50   * already existed within Lang.
51   *
52   * <h2>Known Limitations</h2>
53   * <h3>Accessing Public Methods In A Default Access Superclass</h3>
54   * <p>
55   * There is an issue when invoking {@code public} methods contained in a default access superclass on JREs prior to 1.4.
56   * Reflection locates these methods fine and correctly assigns them as {@code public}.
57   * However, an {@link IllegalAccessException} is thrown if the method is invoked.
58   * </p>
59   *
60   * <p>
61   * {@link MethodUtils} contains a workaround for this situation.
62   * It will attempt to call {@link java.lang.reflect.AccessibleObject#setAccessible(boolean)} on this method.
63   * If this call succeeds, then the method can be invoked as normal.
64   * This call will only succeed when the application has sufficient security privileges.
65   * If this call fails then the method may fail.
66   * </p>
67   *
68   * @since 2.5
69   */
70  public class MethodUtils {
71  
72      private static final Comparator<Method> METHOD_BY_SIGNATURE = Comparator.comparing(Method::toString);
73  
74      /**
75       * Computes the aggregate number of inheritance hops between assignable argument class types.  Returns -1
76       * if the arguments aren't assignable.  Fills a specific purpose for getMatchingMethod and is not generalized.
77       *
78       * @param fromClassArray The Class array to calculate the distance from.
79       * @param toClassArray The Class array to calculate the distance to.
80       * @return The aggregate number of inheritance hops between assignable argument class types.
81       */
82      private static int distance(final Class<?>[] fromClassArray, final Class<?>[] toClassArray) {
83          int answer = 0;
84          if (!ClassUtils.isAssignable(fromClassArray, toClassArray, true)) {
85              return -1;
86          }
87          for (int offset = 0; offset < fromClassArray.length; offset++) {
88              // Note InheritanceUtils.distance() uses different scoring system.
89              final Class<?> aClass = fromClassArray[offset];
90              final Class<?> toClass = toClassArray[offset];
91              if (aClass == null || aClass.equals(toClass)) {
92                  continue;
93              }
94              if (ClassUtils.isAssignable(aClass, toClass, true) && !ClassUtils.isAssignable(aClass, toClass, false)) {
95                  // Autoboxing/unboxing conversion. When a primitive is boxed, rank the exact
96                  // wrapper ahead of any of its supertypes so the most specific overload wins.
97                  answer++;
98                  if (aClass.isPrimitive() && !ClassUtils.primitiveToWrapper(aClass).equals(toClass)) {
99                      answer += 2;
100                 }
101             } else {
102                 answer += 2;
103             }
104         }
105         return answer;
106     }
107 
108     /**
109      * Gets an accessible method (that is, one that can be invoked via reflection) that implements the specified Method. If no such method can be found, return
110      * {@code null}.
111      *
112      * @param cls The implementing class, may be null.
113      * @param method The method that we wish to call, may be null.
114      * @return The accessible method or null.
115      * @since 3.19.0
116      */
117     public static Method getAccessibleMethod(final Class<?> cls, final Method method) {
118         if (!MemberUtils.isPublic(method)) {
119             return null;
120         }
121         // If the declaring class is public, we are done
122         if (ClassUtils.isPublic(cls)) {
123             return method;
124         }
125         final String methodName = method.getName();
126         final Class<?>[] parameterTypes = method.getParameterTypes();
127         // Check the implemented interfaces and subinterfaces
128         final Method method2 = getAccessibleMethodFromInterfaceNest(cls, methodName, parameterTypes);
129         // Check the superclass chain
130         return method2 != null ? method2 : getAccessibleMethodFromSuperclass(cls, methodName, parameterTypes);
131     }
132 
133     /**
134      * 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
135      * {@code null}. This is just a convenience wrapper for {@link #getAccessibleMethod(Method)}.
136      *
137      * @param cls            get method from this class.
138      * @param methodName     get method with this name.
139      * @param parameterTypes with these parameters types.
140      * @return The accessible method.
141      */
142     public static Method getAccessibleMethod(final Class<?> cls, final String methodName, final Class<?>... parameterTypes) {
143         return getAccessibleMethod(getMethodObject(cls, methodName, parameterTypes));
144     }
145 
146     /**
147      * Gets an accessible method (that is, one that can be invoked via reflection) that implements the specified Method. If no such method can be found, return
148      * {@code null}.
149      *
150      * @param method The method that we wish to call, may be null.
151      * @return The accessible method
152      */
153     public static Method getAccessibleMethod(final Method method) {
154         return method != null ? getAccessibleMethod(method.getDeclaringClass(), method) : null;
155     }
156 
157     /**
158      * Gets an accessible method (that is, one that can be invoked via
159      * reflection) that implements the specified method, by scanning through
160      * all implemented interfaces and subinterfaces. If no such method
161      * can be found, return {@code null}.
162      *
163      * <p>
164      * There isn't any good reason why this method must be {@code private}.
165      * It is because there doesn't seem any reason why other classes should
166      * call this rather than the higher level methods.
167      * </p>
168      *
169      * @param cls Parent class for the interfaces to be checked.
170      * @param methodName Method name of the method we wish to call.
171      * @param parameterTypes The parameter type signatures.
172      * @return The accessible method or {@code null} if not found.
173      */
174     private static Method getAccessibleMethodFromInterfaceNest(Class<?> cls, final String methodName, final Class<?>... parameterTypes) {
175         // Search up the superclass chain
176         for (; cls != null; cls = cls.getSuperclass()) {
177             // Check the implemented interfaces of the parent class
178             final Class<?>[] interfaces = cls.getInterfaces();
179             for (final Class<?> anInterface : interfaces) {
180                 // Is this interface public?
181                 if (!ClassUtils.isPublic(anInterface)) {
182                     continue;
183                 }
184                 // Does the method exist on this interface? A static or private one is not inherited.
185                 try {
186                     final Method declared = anInterface.getDeclaredMethod(methodName, parameterTypes);
187                     if (MemberUtils.isPublic(declared) && !MemberUtils.isStatic(declared)) {
188                         return declared;
189                     }
190                 } catch (final NoSuchMethodException ignored) {
191                     /*
192                      * Swallow, if no method is found after the loop then this method returns null.
193                      */
194                 }
195                 // Recursively check our parent interfaces
196                 final Method method = getAccessibleMethodFromInterfaceNest(anInterface, methodName, parameterTypes);
197                 if (method != null) {
198                     return method;
199                 }
200             }
201         }
202         return null;
203     }
204 
205     /**
206      * Gets an accessible method (that is, one that can be invoked via
207      * reflection) by scanning through the superclasses. If no such method
208      * can be found, return {@code null}.
209      *
210      * @param cls Class to be checked.
211      * @param methodName Method name of the method we wish to call.
212      * @param parameterTypes The parameter type signatures.
213      * @return The accessible method or {@code null} if not found.
214      */
215     private static Method getAccessibleMethodFromSuperclass(final Class<?> cls, final String methodName, final Class<?>... parameterTypes) {
216         Class<?> parentClass = cls.getSuperclass();
217         while (parentClass != null) {
218             if (ClassUtils.isPublic(parentClass)) {
219                 return getMethodObject(parentClass, methodName, parameterTypes);
220             }
221             parentClass = parentClass.getSuperclass();
222         }
223         return null;
224     }
225 
226     /**
227      * Gets a combination of {@link ClassUtils#getAllSuperclasses(Class)} and {@link ClassUtils#getAllInterfaces(Class)}, one from superclasses, one from
228      * interfaces, and so on in a breadth first way.
229      *
230      * @param cls The class to look up, may be {@code null}.
231      * @return The combined {@link List} of superclasses and interfaces in order going up from this one {@code null} if null input.
232      */
233     private static List<Class<?>> getAllSuperclassesAndInterfaces(final Class<?> cls) {
234         if (cls == null) {
235             return null;
236         }
237         final List<Class<?>> allSuperClassesAndInterfaces = new ArrayList<>();
238         final List<Class<?>> allSuperclasses = ClassUtils.getAllSuperclasses(cls);
239         int superClassIndex = 0;
240         final List<Class<?>> allInterfaces = ClassUtils.getAllInterfaces(cls);
241         int interfaceIndex = 0;
242         while (interfaceIndex < allInterfaces.size() || superClassIndex < allSuperclasses.size()) {
243             final Class<?> acls;
244             if (interfaceIndex >= allInterfaces.size() || superClassIndex < allSuperclasses.size() && superClassIndex < interfaceIndex) {
245                 acls = allSuperclasses.get(superClassIndex++);
246             } else {
247                 acls = allInterfaces.get(interfaceIndex++);
248             }
249             allSuperClassesAndInterfaces.add(acls);
250         }
251         return allSuperClassesAndInterfaces;
252     }
253 
254     /**
255      * Gets the annotation object with the given annotation type that is present on the given method or optionally on any equivalent method in super classes and
256      * interfaces. Returns null if the annotation type was not present.
257      *
258      * <p>
259      * Stops searching for an annotation once the first annotation of the specified type has been found. Additional annotations of the specified type will be
260      * silently ignored.
261      * </p>
262      *
263      * @param <A>           the annotation type.
264      * @param method        The {@link Method} to query, may be null.
265      * @param annotationCls The {@link Annotation} to check if is present on the method.
266      * @param searchSupers  determines if a lookup in the entire inheritance hierarchy of the given class is performed if the annotation was not directly
267      *                      present.
268      * @param ignoreAccess  determines if underlying method has to be accessible.
269      * @return The first matching annotation, or {@code null} if not found.
270      * @throws NullPointerException Thrown if either the method or annotation class is {@code null}.
271      * @throws SecurityException    Thrown if an underlying accessible object's method denies the request.
272      * @see SecurityManager#checkPermission
273      * @since 3.6
274      */
275     public static <A extends Annotation> A getAnnotation(final Method method, final Class<A> annotationCls, final boolean searchSupers,
276             final boolean ignoreAccess) {
277         Objects.requireNonNull(method, "method");
278         Objects.requireNonNull(annotationCls, "annotationCls");
279         if (!ignoreAccess && !MemberUtils.isAccessible(method)) {
280             return null;
281         }
282         A annotation = method.getAnnotation(annotationCls);
283         if (annotation == null && searchSupers) {
284             final Class<?> mcls = method.getDeclaringClass();
285             final String methodName = method.getName();
286             final Class<?>[] paramTypes = method.getParameterTypes();
287             final List<Class<?>> classes = getAllSuperclassesAndInterfaces(mcls);
288             for (final Class<?> acls : classes) {
289                 // First, attempt an exact parameter-type match (getDeclaredMethod) to
290                 // find a true override. This avoids matching unrelated overloads that
291                 // are merely assignable-compatible (e.g. process(Integer) vs
292                 // process(Number)).
293                 Method equivalentMethod = null;
294                 try {
295                     equivalentMethod = acls.getDeclaredMethod(methodName, paramTypes);
296                 } catch (final NoSuchMethodException ignored) {
297                     // No exact match; check for generic-bridge scenario: the declaring
298                     // class may use a type variable whose erased form is Object (or
299                     // another bound). In that case the parent method's erased
300                     // parameter types differ from the child's concrete types, so we
301                     // scan declared methods for a same-name method whose *erased*
302                     // parameter count matches and whose erased types are assignable
303                     // from our concrete types.
304                     for (final Method candidate : acls.getDeclaredMethods()) {
305                         if (!candidate.getName().equals(methodName)) {
306                             continue;
307                         }
308                         final Class<?>[] candidateParams = candidate.getParameterTypes();
309                         if (candidateParams.length != paramTypes.length) {
310                             continue;
311                         }
312                         // Require that every concrete param type is assignable to the
313                         // candidate's (erased) param type AND that the candidate is
314                         // generic (has at least one TypeVariable in its generic
315                         // parameter types). This prevents matching plain overloads.
316                         boolean genericMatch = false;
317                         boolean paramsMatch = true;
318                         final java.lang.reflect.Type[] genericParams = candidate.getGenericParameterTypes();
319                         for (int i = 0; i < candidateParams.length; i++) {
320                             if (genericParams[i] instanceof java.lang.reflect.TypeVariable) {
321                                 genericMatch = true;
322                             }
323                             if (!ClassUtils.isAssignable(paramTypes[i], candidateParams[i], true)) {
324                                 paramsMatch = false;
325                                 break;
326                             }
327                         }
328                         if (paramsMatch && genericMatch) {
329                             equivalentMethod = candidate;
330                             break;
331                         }
332                     }
333                 }
334                 if (equivalentMethod != null && (ignoreAccess || MemberUtils.isAccessible(equivalentMethod))) {
335                     annotation = equivalentMethod.getAnnotation(annotationCls);
336                     if (annotation != null) {
337                         break;
338                     }
339                 }
340             }
341         }
342         return annotation;
343     }
344 
345     private static Method getInvokeMethod(final boolean forceAccess, final String methodName, final Class<?>[] parameterTypes, final Class<?> cls) {
346         final Method method;
347         if (forceAccess) {
348             method = getMatchingMethod(cls, methodName, parameterTypes);
349             AccessibleObjects.setAccessible(method);
350         } else {
351             method = getMatchingAccessibleMethod(cls, methodName, parameterTypes);
352         }
353         return method;
354     }
355 
356     /**
357      * Gets an accessible method that matches the given name and has compatible parameters. Compatible parameters mean that every method parameter is assignable
358      * from the given parameters. In other words, it finds a method with the given name that will take the parameters given.
359      *
360      * <p>
361      * This method is used by {@link #invokeMethod(Object object, String methodName, Object[] args, Class[] parameterTypes)}.
362      * </p>
363      * <p>
364      * This method can match primitive parameter by passing in wrapper classes. For example, a {@link Boolean} will match a primitive {@code boolean} parameter.
365      * </p>
366      *
367      * @param cls            find method in this class.
368      * @param methodName     find method with this name.
369      * @param requestTypes find method with most compatible parameters.
370      * @return The accessible method or null.
371      * @throws SecurityException Thrown if an underlying accessible object's method denies the request.
372      * @see SecurityManager#checkPermission
373      */
374     public static Method getMatchingAccessibleMethod(final Class<?> cls, final String methodName, final Class<?>... requestTypes) {
375         final Method candidate = getMethodObject(cls, methodName, requestTypes);
376         if (candidate != null) {
377             // The exact match may be declared on a non-public class, so prefer the public
378             // declaration the way the search below does; a static method hides, so it is kept.
379             final Method accessibleCandidate = MemberUtils.isStatic(candidate) ? null : getAccessibleMethod(cls, candidate);
380             return MemberUtils.setAccessibleWorkaround(accessibleCandidate != null ? accessibleCandidate : candidate);
381         }
382         // search through all methods
383         final Method[] methods = cls.getMethods();
384         final List<Method> matchingMethods = Stream.of(methods)
385                 .filter(method -> method.getName().equals(methodName) && MemberUtils.isMatchingMethod(method, requestTypes)).collect(Collectors.toList());
386         // Sort methods by signature to force deterministic result
387         matchingMethods.sort(METHOD_BY_SIGNATURE);
388         Method bestMatch = null;
389         for (final Method method : matchingMethods) {
390             // get accessible version of method
391             final Method accessibleMethod = getAccessibleMethod(method);
392             if (accessibleMethod != null && (bestMatch == null || MemberUtils.compareMethodFit(accessibleMethod, bestMatch, requestTypes) < 0)) {
393                 bestMatch = accessibleMethod;
394             }
395         }
396         if (bestMatch != null) {
397             MemberUtils.setAccessibleWorkaround(bestMatch);
398             if (bestMatch.isVarArgs()) {
399                 final Class<?>[] bestMatchParameterTypes = bestMatch.getParameterTypes();
400                 final Class<?> varArgType = bestMatchParameterTypes[bestMatchParameterTypes.length - 1].getComponentType();
401                 for (int paramIdx = bestMatchParameterTypes.length - 1; paramIdx < requestTypes.length; paramIdx++) {
402                     final Class<?> parameterType = requestTypes[paramIdx];
403                     if (!ClassUtils.isAssignable(parameterType, varArgType, true)) {
404                         return null;
405                     }
406                 }
407             }
408         }
409         return bestMatch;
410     }
411 
412     /**
413      * Gets a method whether or not it's accessible. If no such method can be found, return {@code null}.
414      *
415      * @param cls            The class that will be subjected to the method search.
416      * @param methodName     The method that we wish to call.
417      * @param parameterTypes Argument class types.
418      * @throws IllegalStateException Thrown if there is no unique result.
419      * @throws NullPointerException  Thrown if the class is {@code null}.
420      * @return The method.
421      * @since 3.5
422      */
423     public static Method getMatchingMethod(final Class<?> cls, final String methodName, final Class<?>... parameterTypes) {
424         Objects.requireNonNull(cls, "cls");
425         Validate.notEmpty(methodName, "methodName");
426         final List<Method> methods = Stream.of(cls.getDeclaredMethods())
427                 .filter(method -> method.getName().equals(methodName))
428                 .collect(Collectors.toList());
429         final List<Class<?>> allSuperclassesAndInterfaces = getAllSuperclassesAndInterfaces(cls);
430         Collections.reverse(allSuperclassesAndInterfaces);
431         allSuperclassesAndInterfaces.stream()
432                 .map(Class::getDeclaredMethods)
433                 .flatMap(Stream::of)
434                 .filter(method -> method.getName().equals(methodName))
435                 .forEach(methods::add);
436         for (final Method method : methods) {
437             if (Arrays.deepEquals(method.getParameterTypes(), parameterTypes)) {
438                 return method;
439             }
440         }
441         final TreeMap<Integer, List<Method>> candidates = new TreeMap<>();
442         methods.stream()
443             .filter(method -> ClassUtils.isAssignable(parameterTypes, method.getParameterTypes(), true))
444             .forEach(method -> {
445                  final int distance = distance(parameterTypes, method.getParameterTypes());
446                  final List<Method> candidatesAtDistance = candidates.computeIfAbsent(distance, k -> new ArrayList<>());
447                  candidatesAtDistance.add(method);
448         });
449         if (candidates.isEmpty()) {
450             return null;
451         }
452         final List<Method> bestCandidates = candidates.values().iterator().next();
453         if (bestCandidates.size() == 1 || !Objects.equals(bestCandidates.get(0).getDeclaringClass(),
454                 bestCandidates.get(1).getDeclaringClass())) {
455             return bestCandidates.get(0);
456         }
457         throw new IllegalStateException(String.format("Found multiple candidates for method %s on class %s : %s",
458                 methodName + Stream.of(parameterTypes).map(String::valueOf).collect(Collectors.joining(",", "(", ")")), cls.getName(),
459                 bestCandidates.stream().map(Method::toString).collect(Collectors.joining(",", "[", "]"))));
460     }
461 
462     /**
463      * Gets a Method, or {@code null} if a checked {@link Class#getMethod(String, Class...) } exception is thrown.
464      *
465      * @param cls            Receiver for {@link Class#getMethod(String, Class...)}.
466      * @param name           The name of the method.
467      * @param parameterTypes The list of parameters.
468      * @return A Method or {@code null}.
469      * @see SecurityManager#checkPermission
470      * @see Class#getMethod(String, Class...)
471      * @since 3.15.0
472      */
473     public static Method getMethodObject(final Class<?> cls, final String name, final Class<?>... parameterTypes) {
474         try {
475             return name != null && cls != null ? cls.getMethod(name, parameterTypes) : null;
476         } catch (final NoSuchMethodException | SecurityException e) {
477             return null;
478         }
479     }
480 
481     /**
482      * Gets all class level public methods of the given class that are annotated with the given annotation.
483      *
484      * @param cls           The {@link Class} to query.
485      * @param annotationCls The {@link Annotation} that must be present on a method to be matched.
486      * @return A list of Methods (possibly empty).
487      * @throws NullPointerException Thrown if the class or annotation are {@code null}.
488      * @since 3.4
489      */
490     public static List<Method> getMethodsListWithAnnotation(final Class<?> cls, final Class<? extends Annotation> annotationCls) {
491         return getMethodsListWithAnnotation(cls, annotationCls, false, false);
492     }
493 
494     /**
495      * Gets all methods of the given class that are annotated with the given annotation.
496      *
497      * @param cls           The {@link Class} to query.
498      * @param annotationCls The {@link Annotation} that must be present on a method to be matched.
499      * @param searchSupers  determines if a lookup in the entire inheritance hierarchy of the given class should be performed.
500      * @param ignoreAccess  determines if non-public methods should be considered.
501      * @return A list of Methods (possibly empty).
502      * @throws NullPointerException Thrown if either the class or annotation class is {@code null}.
503      * @since 3.6
504      */
505     public static List<Method> getMethodsListWithAnnotation(final Class<?> cls, final Class<? extends Annotation> annotationCls, final boolean searchSupers,
506             final boolean ignoreAccess) {
507         Objects.requireNonNull(cls, "cls");
508         Objects.requireNonNull(annotationCls, "annotationCls");
509         final List<Class<?>> classes = searchSupers ? getAllSuperclassesAndInterfaces(cls) : new ArrayList<>();
510         classes.add(0, cls);
511         final List<Method> annotatedMethods = new ArrayList<>();
512         classes.forEach(acls -> {
513             final Method[] methods = ignoreAccess ? acls.getDeclaredMethods() : acls.getMethods();
514             Stream.of(methods).filter(method -> method.isAnnotationPresent(annotationCls)).forEachOrdered(annotatedMethods::add);
515         });
516         return annotatedMethods;
517     }
518 
519     /**
520      * Gets all class level public methods of the given class that are annotated with the given annotation.
521      *
522      * @param cls           The {@link Class} to query.
523      * @param annotationCls The {@link java.lang.annotation.Annotation} that must be present on a method to be matched.
524      * @return An array of Methods (possibly empty).
525      * @throws NullPointerException Thrown if the class or annotation are {@code null}.
526      * @since 3.4
527      */
528     public static Method[] getMethodsWithAnnotation(final Class<?> cls, final Class<? extends Annotation> annotationCls) {
529         return getMethodsWithAnnotation(cls, annotationCls, false, false);
530     }
531 
532     /**
533      * Gets all methods of the given class that are annotated with the given annotation.
534      *
535      * @param cls           The {@link Class} to query.
536      * @param annotationCls The {@link java.lang.annotation.Annotation} that must be present on a method to be matched.
537      * @param searchSupers  determines if a lookup in the entire inheritance hierarchy of the given class should be performed.
538      * @param ignoreAccess  determines if non-public methods should be considered.
539      * @return An array of Methods (possibly empty).
540      * @throws NullPointerException Thrown if the class or annotation are {@code null}.
541      * @since 3.6
542      */
543     public static Method[] getMethodsWithAnnotation(final Class<?> cls, final Class<? extends Annotation> annotationCls, final boolean searchSupers,
544             final boolean ignoreAccess) {
545         return getMethodsListWithAnnotation(cls, annotationCls, searchSupers, ignoreAccess).toArray(ArrayUtils.EMPTY_METHOD_ARRAY);
546     }
547 
548     /**
549      * Gets the hierarchy of overridden methods down to {@code result} respecting generics.
550      *
551      * @param method lowest to consider.
552      * @param interfacesBehavior whether to search interfaces, {@code null} {@code implies} false.
553      * @return A {@code Set<Method>} in ascending order from subclass to superclass.
554      * @throws NullPointerException Thrown if the specified method is {@code null}.
555      * @throws SecurityException Thrown if an underlying accessible object's method denies the request.
556      * @see SecurityManager#checkPermission
557      * @since 3.2
558      */
559     public static Set<Method> getOverrideHierarchy(final Method method, final Interfaces interfacesBehavior) {
560         Objects.requireNonNull(method, "method");
561         final Set<Method> result = new LinkedHashSet<>();
562         result.add(method);
563         final Class<?>[] parameterTypes = method.getParameterTypes();
564         final Class<?> declaringClass = method.getDeclaringClass();
565         final Iterator<Class<?>> hierarchy = ClassUtils.hierarchy(declaringClass, interfacesBehavior).iterator();
566         //skip the declaring class :P
567         hierarchy.next();
568         hierarchyTraversal: while (hierarchy.hasNext()) {
569             final Class<?> c = hierarchy.next();
570             final Method m = getMatchingAccessibleMethod(c, method.getName(), parameterTypes);
571             if (m == null) {
572                 continue;
573             }
574             if (Arrays.equals(m.getParameterTypes(), parameterTypes)) {
575                 // matches without generics
576                 result.add(m);
577                 continue;
578             }
579             // necessary to get arguments every time in the case that we are including interfaces
580             final Map<TypeVariable<?>, Type> typeArguments = TypeUtils.getTypeArguments(declaringClass, m.getDeclaringClass());
581             for (int i = 0; i < parameterTypes.length; i++) {
582                 final Type childType = TypeUtils.unrollVariables(typeArguments, method.getGenericParameterTypes()[i]);
583                 final Type parentType = TypeUtils.unrollVariables(typeArguments, m.getGenericParameterTypes()[i]);
584                 if (!TypeUtils.equals(childType, parentType)) {
585                     continue hierarchyTraversal;
586                 }
587             }
588             result.add(m);
589         }
590         return result;
591     }
592 
593     /**
594      * Invokes a method whose parameter types match exactly the object type.
595      *
596      * <p>
597      * This uses reflection to invoke the method obtained from a call to {@link #getAccessibleMethod(Class, String, Class[])}.
598      * </p>
599      *
600      * @param object     invoke method on this object.
601      * @param methodName get method with this name.
602      * @return The value returned by the invoked method.
603      * @throws NoSuchMethodException       Thrown if there is no such accessible method.
604      * @throws IllegalAccessException      Thrown if this found {@code Method} is enforcing Java language access control and the underlying method is
605      *                                     inaccessible.
606      * @throws IllegalArgumentException    Thrown if:
607      *                                     <ul>
608      *                                     <li>the found {@code Method} is an instance method and the specified {@code object} argument is not an instance of
609      *                                     the class or interface declaring the underlying method (or of a subclass or interface implementor);</li>
610      *                                     <li>the number of actual and formal parameters differ;</li>
611      *                                     </ul>
612      * @throws InvocationTargetException   Thrown if the underlying method throws an exception.
613      * @throws NullPointerException        Thrown if the specified {@code object} is null.
614      * @throws ExceptionInInitializerError Thrown if the initialization provoked by this method fails.
615      * @since 3.4
616      */
617     public static Object invokeExactMethod(final Object object, final String methodName)
618             throws NoSuchMethodException, IllegalAccessException, InvocationTargetException {
619         return invokeExactMethod(object, methodName, ArrayUtils.EMPTY_OBJECT_ARRAY, null);
620     }
621 
622     /**
623      * Invokes a method whose parameter types match exactly the object types.
624      *
625      * <p>
626      * This uses reflection to invoke the method obtained from a call to {@link #getAccessibleMethod(Class, String, Class[])}.
627      * </p>
628      *
629      * @param object     invoke method on this object.
630      * @param methodName get method with this name.
631      * @param args       use these arguments - treat null as empty array.
632      * @return The value returned by the invoked method.
633      * @throws NoSuchMethodException       Thrown if there is no such accessible method.
634      * @throws IllegalAccessException      Thrown if this found {@code Method} is enforcing Java language access control and the underlying method is
635      *                                     inaccessible.
636      * @throws IllegalArgumentException    Thrown if:
637      *                                     <ul>
638      *                                     <li>the found {@code Method} is an instance method and the specified {@code object} argument is not an instance of
639      *                                     the class or interface declaring the underlying method (or of a subclass or interface implementor);</li>
640      *                                     <li>the number of actual and formal parameters differ;</li>
641      *                                     <li>an unwrapping conversion for primitive arguments fails; or</li>
642      *                                     <li>after possible unwrapping, a parameter value can't be converted to the corresponding formal parameter type by a
643      *                                     method invocation conversion.</li>
644      *                                     </ul>
645      * @throws InvocationTargetException   Thrown if the underlying method throws an exception.
646      * @throws NullPointerException        Thrown if the specified {@code object} is null.
647      * @throws ExceptionInInitializerError Thrown if the initialization provoked by this method fails.
648      */
649     public static Object invokeExactMethod(final Object object, final String methodName, final Object... args)
650             throws NoSuchMethodException, IllegalAccessException, InvocationTargetException {
651         final Object[] actuals = ArrayUtils.nullToEmpty(args);
652         return invokeExactMethod(object, methodName, actuals, ClassUtils.toClass(actuals));
653     }
654 
655     /**
656      * Invokes a method whose parameter types match exactly the parameter types given.
657      *
658      * <p>
659      * This uses reflection to invoke the method obtained from a call to {@link #getAccessibleMethod(Class, String, Class[])}.
660      * </p>
661      *
662      * @param object         Invokes a method on this object.
663      * @param methodName     Gets a method with this name.
664      * @param args           Method arguments - treat null as empty array.
665      * @param parameterTypes Match these parameters - treat {@code null} as empty array.
666      * @return The value returned by the invoked method.
667      * @throws NoSuchMethodException       Thrown if there is no such accessible method.
668      * @throws IllegalAccessException      Thrown if this found {@code Method} is enforcing Java language access control and the underlying method is
669      *                                     inaccessible.
670      * @throws IllegalArgumentException    Thrown if:
671      *                                     <ul>
672      *                                     <li>the found {@code Method} is an instance method and the specified {@code object} argument is not an instance of
673      *                                     the class or interface declaring the underlying method (or of a subclass or interface implementor);</li>
674      *                                     <li>the number of actual and formal parameters differ;</li>
675      *                                     <li>an unwrapping conversion for primitive arguments fails; or</li>
676      *                                     <li>after possible unwrapping, a parameter value can't be converted to the corresponding formal parameter type by a
677      *                                     method invocation conversion.</li>
678      *                                     </ul>
679      * @throws InvocationTargetException   Thrown if the underlying method throws an exception.
680      * @throws NullPointerException        Thrown if the specified {@code object} is null.
681      * @throws ExceptionInInitializerError Thrown if the initialization provoked by this method fails.
682      */
683     public static Object invokeExactMethod(final Object object, final String methodName, final Object[] args, final Class<?>[] parameterTypes)
684             throws NoSuchMethodException, IllegalAccessException, InvocationTargetException {
685         final Class<?> cls = Objects.requireNonNull(object, "object").getClass();
686         final Class<?>[] paramTypes = ArrayUtils.nullToEmpty(parameterTypes);
687         final Method method = getAccessibleMethod(cls, methodName, paramTypes);
688         requireNonNull(method, cls, methodName, paramTypes);
689         return method.invoke(object, ArrayUtils.nullToEmpty(args));
690     }
691 
692     /**
693      * Invokes a {@code static} method whose parameter types match exactly the object types.
694      *
695      * <p>
696      * This uses reflection to invoke the method obtained from a call to {@link #getAccessibleMethod(Class, String, Class[])}.
697      * </p>
698      *
699      * @param cls        invoke static method on this class.
700      * @param methodName get method with this name.
701      * @param args       use these arguments - treat {@code null} as empty array.
702      * @return The value returned by the invoked method.
703      * @throws NoSuchMethodException       Thrown if there is no such accessible method.
704      * @throws IllegalAccessException      Thrown if this found {@code Method} is enforcing Java language access control and the underlying method is
705      *                                     inaccessible.
706      * @throws IllegalArgumentException    Thrown if:
707      *                                     <ul>
708      *                                     <li>the found {@code Method} is an instance method and the specified {@code object} argument is not an instance of
709      *                                     the class or interface declaring the underlying method (or of a subclass or interface implementor);</li>
710      *                                     <li>the number of actual and formal parameters differ;</li>
711      *                                     <li>an unwrapping conversion for primitive arguments fails; or</li>
712      *                                     <li>after possible unwrapping, a parameter value can't be converted to the corresponding formal parameter type by a
713      *                                     method invocation conversion.</li>
714      *                                     </ul>
715      * @throws InvocationTargetException   Thrown if the underlying method throws an exception.
716      * @throws ExceptionInInitializerError Thrown if the initialization provoked by this method fails.
717      */
718     public static Object invokeExactStaticMethod(final Class<?> cls, final String methodName, final Object... args)
719             throws NoSuchMethodException, IllegalAccessException, InvocationTargetException {
720         final Object[] actuals = ArrayUtils.nullToEmpty(args);
721         return invokeExactStaticMethod(cls, methodName, actuals, ClassUtils.toClass(actuals));
722     }
723 
724     /**
725      * Invokes a {@code static} method whose parameter types match exactly the parameter types given.
726      *
727      * <p>
728      * This uses reflection to invoke the method obtained from a call to {@link #getAccessibleMethod(Class, String, Class[])}.
729      * </p>
730      *
731      * @param cls            invoke static method on this class.
732      * @param methodName     get method with this name.
733      * @param args           use these arguments - treat {@code null} as empty array.
734      * @param parameterTypes match these parameters - treat {@code null} as empty array.
735      * @return The value returned by the invoked method.
736      * @throws NoSuchMethodException       Thrown if there is no such accessible method.
737      * @throws IllegalAccessException      Thrown if this found {@code Method} is enforcing Java language access control and the underlying method is
738      *                                     inaccessible.
739      * @throws IllegalArgumentException    Thrown if:
740      *                                     <ul>
741      *                                     <li>the found {@code Method} is an instance method and the specified {@code object} argument is not an instance of
742      *                                     the class or interface declaring the underlying method (or of a subclass or interface implementor);</li>
743      *                                     <li>the number of actual and formal parameters differ;</li>
744      *                                     <li>an unwrapping conversion for primitive arguments fails; or</li>
745      *                                     <li>after possible unwrapping, a parameter value can't be converted to the corresponding formal parameter type by a
746      *                                     method invocation conversion.</li>
747      *                                     </ul>
748      * @throws InvocationTargetException   Thrown if the underlying method throws an exception.
749      * @throws ExceptionInInitializerError Thrown if the initialization provoked by this method fails.
750      */
751     public static Object invokeExactStaticMethod(final Class<?> cls, final String methodName, final Object[] args, final Class<?>[] parameterTypes)
752             throws NoSuchMethodException, IllegalAccessException, InvocationTargetException {
753         final Class<?>[] paramTypes = ArrayUtils.nullToEmpty(parameterTypes);
754         final Method method = getAccessibleMethod(cls, methodName, ArrayUtils.nullToEmpty(paramTypes));
755         requireNonNull(method, cls, methodName, paramTypes);
756         return method.invoke(null, ArrayUtils.nullToEmpty(args));
757     }
758 
759     /**
760      * Invokes a named method without parameters.
761      *
762      * <p>
763      * This is a convenient wrapper for
764      * {@link #invokeMethod(Object object, boolean forceAccess, String methodName, Object[] args, Class[] parameterTypes)}.
765      * </p>
766      *
767      * @param object invoke method on this object.
768      * @param forceAccess force access to invoke method even if it's not accessible.
769      * @param methodName get method with this name.
770      * @return The value returned by the invoked method.
771      * @throws NoSuchMethodException       Thrown if there is no such accessible method.
772      * @throws IllegalAccessException      Thrown if this found {@code Method} is enforcing Java language access control and the underlying method is
773      *                                     inaccessible.
774      * @throws IllegalArgumentException    Thrown if:
775      *                                     <ul>
776      *                                     <li>the found {@code Method} is an instance method and the specified {@code object} argument is not an instance of
777      *                                     the class or interface declaring the underlying method (or of a subclass or interface implementor);</li>
778      *                                     <li>the number of actual and formal parameters differ;</li>
779      *                                     <li>an unwrapping conversion for primitive arguments fails; or</li>
780      *                                     <li>after possible unwrapping, a parameter value can't be converted to the corresponding formal parameter type by a
781      *                                     method invocation conversion.</li>
782      *                                     </ul>
783      * @throws InvocationTargetException   Thrown if the underlying method throws an exception.
784      * @throws NullPointerException        Thrown if the specified {@code object} is null.
785      * @throws ExceptionInInitializerError Thrown if the initialization provoked by this method fails.
786      * @see SecurityManager#checkPermission
787      * @since 3.5
788      */
789     public static Object invokeMethod(final Object object, final boolean forceAccess, final String methodName)
790             throws NoSuchMethodException, IllegalAccessException, InvocationTargetException {
791         return invokeMethod(object, forceAccess, methodName, ArrayUtils.EMPTY_OBJECT_ARRAY, null);
792     }
793 
794     /**
795      * Invokes a named method whose parameter type matches the object type.
796      *
797      * <p>
798      * This method supports calls to methods taking primitive parameters
799      * via passing in wrapping classes. So, for example, a {@link Boolean} object
800      * would match a {@code boolean} primitive.
801      * </p>
802      * <p>
803      * This is a convenient wrapper for
804      * {@link #invokeMethod(Object object, boolean forceAccess, String methodName, Object[] args, Class[] parameterTypes)}.
805      * </p>
806      *
807      * @param object invoke method on this object.
808      * @param forceAccess force access to invoke method even if it's not accessible.
809      * @param methodName get method with this name.
810      * @param args use these arguments - treat null as empty array.
811      * @return The value returned by the invoked method.
812      * @throws NoSuchMethodException       Thrown if there is no such accessible method.
813      * @throws IllegalAccessException      Thrown if this found {@code Method} is enforcing Java language access control and the underlying method is
814      *                                     inaccessible.
815      * @throws IllegalArgumentException    Thrown if:
816      *                                     <ul>
817      *                                     <li>the found {@code Method} is an instance method and the specified {@code object} argument is not an instance of
818      *                                     the class or interface declaring the underlying method (or of a subclass or interface implementor);</li>
819      *                                     <li>the number of actual and formal parameters differ;</li>
820      *                                     <li>an unwrapping conversion for primitive arguments fails; or</li>
821      *                                     <li>after possible unwrapping, a parameter value can't be converted to the corresponding formal parameter type by a
822      *                                     method invocation conversion.</li>
823      *                                     </ul>
824      * @throws InvocationTargetException   Thrown if the underlying method throws an exception.
825      * @throws NullPointerException        Thrown if the specified {@code object} is null.
826      * @throws ExceptionInInitializerError Thrown if the initialization provoked by this method fails.
827      * @see SecurityManager#checkPermission
828      * @since 3.5
829      */
830     public static Object invokeMethod(final Object object, final boolean forceAccess, final String methodName, final Object... args)
831             throws NoSuchMethodException, IllegalAccessException, InvocationTargetException {
832         final Object[] actuals = ArrayUtils.nullToEmpty(args);
833         return invokeMethod(object, forceAccess, methodName, actuals, ClassUtils.toClass(actuals));
834     }
835 
836     /**
837      * Invokes a named method whose parameter type matches the object type.
838      *
839      * <p>
840      * This method supports calls to methods taking primitive parameters
841      * via passing in wrapping classes. So, for example, a {@link Boolean} object
842      * would match a {@code boolean} primitive.
843      * </p>
844      *
845      * @param object invoke method on this object.
846      * @param forceAccess force access to invoke method even if it's not accessible.
847      * @param methodName get method with this name.
848      * @param args use these arguments - treat null as empty array.
849      * @param parameterTypes match these parameters - treat null as empty array.
850      * @return The value returned by the invoked method.
851      * @throws NoSuchMethodException       Thrown if there is no such accessible method.
852      * @throws IllegalAccessException      Thrown if this found {@code Method} is enforcing Java language access control and the underlying method is
853      *                                     inaccessible.
854      * @throws IllegalArgumentException    Thrown if:
855      *                                     <ul>
856      *                                     <li>the found {@code Method} is an instance method and the specified {@code object} argument is not an instance of
857      *                                     the class or interface declaring the underlying method (or of a subclass or interface implementor);</li>
858      *                                     <li>the number of actual and formal parameters differ;</li>
859      *                                     <li>an unwrapping conversion for primitive arguments fails; or</li>
860      *                                     <li>after possible unwrapping, a parameter value can't be converted to the corresponding formal parameter type by a
861      *                                     method invocation conversion.</li>
862      *                                     </ul>
863      * @throws InvocationTargetException   Thrown if the underlying method throws an exception.
864      * @throws NullPointerException        Thrown if the specified {@code object} is null.
865      * @throws ExceptionInInitializerError Thrown if the initialization provoked by this method fails.
866      * @see SecurityManager#checkPermission
867      * @since 3.5
868      */
869     public static Object invokeMethod(final Object object, final boolean forceAccess, final String methodName, final Object[] args, final Class<?>[] parameterTypes)
870             throws NoSuchMethodException, IllegalAccessException, InvocationTargetException {
871         final Class<?> cls = Objects.requireNonNull(object, "object").getClass();
872         final Class<?>[] paramTypes = ArrayUtils.nullToEmpty(parameterTypes);
873         final Method method = getInvokeMethod(forceAccess, methodName, paramTypes, cls);
874         requireNonNull(method, cls, methodName, paramTypes);
875         return method.invoke(object, toVarArgs(method, ArrayUtils.nullToEmpty(args)));
876     }
877 
878     /**
879      * Invokes a named method without parameters.
880      *
881      * <p>
882      * This method delegates the method search to {@link #getMatchingAccessibleMethod(Class, String, Class[])}.
883      * </p>
884      * <p>
885      * This is a convenient wrapper for
886      * {@link #invokeMethod(Object object, String methodName, Object[] args, Class[] parameterTypes)}.
887      * </p>
888      *
889      * @param object invoke method on this object.
890      * @param methodName get method with this name.
891      * @return The value returned by the invoked method.
892      * @throws NoSuchMethodException Thrown if there is no such accessible method.
893      * @throws InvocationTargetException Thrown to wrap an exception thrown by the method invoked.
894      * @throws IllegalAccessException Thrown if the requested method is not accessible via reflection.
895      * @throws SecurityException Thrown if an underlying accessible object's method denies the request.
896      * @see SecurityManager#checkPermission
897      * @since 3.4
898      */
899     public static Object invokeMethod(final Object object, final String methodName) throws NoSuchMethodException,
900             IllegalAccessException, InvocationTargetException {
901         return invokeMethod(object, methodName, ArrayUtils.EMPTY_OBJECT_ARRAY, null);
902     }
903 
904     /**
905      * Invokes a named method whose parameter type matches the object type.
906      *
907      * <p>
908      * This method delegates the method search to {@link #getMatchingAccessibleMethod(Class, String, Class[])}.
909      * </p>
910      * <p>
911      * This method supports calls to methods taking primitive parameters
912      * via passing in wrapping classes. So, for example, a {@link Boolean} object
913      * would match a {@code boolean} primitive.
914      * </p>
915      * <p>
916      * This is a convenient wrapper for
917      * {@link #invokeMethod(Object object, String methodName, Object[] args, Class[] parameterTypes)}.
918      * </p>
919      *
920      * @param object invoke method on this object.
921      * @param methodName get method with this name.
922      * @param args use these arguments - treat null as empty array.
923      * @return The value returned by the invoked method.
924      * @throws NoSuchMethodException Thrown if there is no such accessible method.
925      * @throws InvocationTargetException Thrown to wrap an exception thrown by the method invoked.
926      * @throws IllegalAccessException Thrown if the requested method is not accessible via reflection.
927      * @throws NullPointerException Thrown if the object or method name are {@code null}.
928      * @throws SecurityException Thrown if an underlying accessible object's method denies the request.
929      * @see SecurityManager#checkPermission
930      */
931     public static Object invokeMethod(final Object object, final String methodName, final Object... args)
932             throws NoSuchMethodException, IllegalAccessException, InvocationTargetException {
933         final Object[] actuals = ArrayUtils.nullToEmpty(args);
934         return invokeMethod(object, methodName, actuals, ClassUtils.toClass(actuals));
935     }
936 
937     /**
938      * Invokes a named method whose parameter type matches the object type.
939      *
940      * <p>
941      * This method delegates the method search to {@link #getMatchingAccessibleMethod(Class, String, Class[])}.
942      * </p>
943      * <p>
944      * This method supports calls to methods taking primitive parameters
945      * via passing in wrapping classes. So, for example, a {@link Boolean} object
946      * would match a {@code boolean} primitive.
947      * </p>
948      *
949      * @param object invoke method on this object.
950      * @param methodName get method with this name.
951      * @param args use these arguments - treat null as empty array.
952      * @param parameterTypes match these parameters - treat null as empty array.
953      * @return The value returned by the invoked method.
954      * @throws NoSuchMethodException       Thrown if there is no such accessible method.
955      * @throws IllegalAccessException      Thrown if this found {@code Method} is enforcing Java language access control and the underlying method is
956      *                                     inaccessible.
957      * @throws IllegalArgumentException    Thrown if:
958      *                                     <ul>
959      *                                     <li>the found {@code Method} is an instance method and the specified {@code object} argument is not an instance of
960      *                                     the class or interface declaring the underlying method (or of a subclass or interface implementor);</li>
961      *                                     <li>the number of actual and formal parameters differ;</li>
962      *                                     <li>an unwrapping conversion for primitive arguments fails; or</li>
963      *                                     <li>after possible unwrapping, a parameter value can't be converted to the corresponding formal parameter type by a
964      *                                     method invocation conversion.</li>
965      *                                     </ul>
966      * @throws InvocationTargetException   Thrown if the underlying method throws an exception.
967      * @throws NullPointerException        Thrown if the specified {@code object} is null.
968      * @throws ExceptionInInitializerError Thrown if the initialization provoked by this method fails.
969      * @see SecurityManager#checkPermission
970      */
971     public static Object invokeMethod(final Object object, final String methodName, final Object[] args, final Class<?>[] parameterTypes)
972             throws NoSuchMethodException, IllegalAccessException, InvocationTargetException {
973         return invokeMethod(object, false, methodName, args, parameterTypes);
974     }
975 
976     /**
977      * Invokes a named {@code static} method whose parameter type matches the object type.
978      *
979      * <p>
980      * This method delegates the method search to {@link #getMatchingAccessibleMethod(Class, String, Class[])}.
981      * </p>
982      * <p>
983      * This method supports calls to methods taking primitive parameters
984      * via passing in wrapping classes. So, for example, a {@link Boolean} class
985      * would match a {@code boolean} primitive.
986      * </p>
987      * <p>
988      * This is a convenient wrapper for
989      * {@link #invokeStaticMethod(Class, String, Object[], Class[])}.
990      * </p>
991      *
992      * @param cls invoke static method on this class.
993      * @param methodName get method with this name.
994      * @param args use these arguments - treat {@code null} as empty array.
995      * @return The value returned by the invoked method.
996      * @throws NoSuchMethodException       Thrown if there is no such accessible method.
997      * @throws IllegalAccessException      Thrown if this found {@code Method} is enforcing Java language access control and the underlying method is
998      *                                     inaccessible.
999      * @throws IllegalArgumentException    Thrown if:
1000      *                                     <ul>
1001      *                                     <li>the found {@code Method} is an instance method and the specified {@code object} argument is not an instance of
1002      *                                     the class or interface declaring the underlying method (or of a subclass or interface implementor);</li>
1003      *                                     <li>the number of actual and formal parameters differ;</li>
1004      *                                     <li>an unwrapping conversion for primitive arguments fails; or</li>
1005      *                                     <li>after possible unwrapping, a parameter value can't be converted to the corresponding formal parameter type by a
1006      *                                     method invocation conversion.</li>
1007      *                                     </ul>
1008      * @throws InvocationTargetException   Thrown if the underlying method throws an exception.
1009      * @throws ExceptionInInitializerError Thrown if the initialization provoked by this method fails.
1010      * @see SecurityManager#checkPermission
1011      */
1012     public static Object invokeStaticMethod(final Class<?> cls, final String methodName, final Object... args)
1013             throws NoSuchMethodException, IllegalAccessException, InvocationTargetException {
1014         final Object[] actuals = ArrayUtils.nullToEmpty(args);
1015         return invokeStaticMethod(cls, methodName, actuals, ClassUtils.toClass(actuals));
1016     }
1017 
1018     /**
1019      * Invokes a named {@code static} method whose parameter type matches the object type.
1020      *
1021      * <p>
1022      * This method delegates the method search to {@link #getMatchingAccessibleMethod(Class, String, Class[])}.
1023      * </p>
1024      * <p>
1025      * This method supports calls to methods taking primitive parameters
1026      * via passing in wrapping classes. So, for example, a {@link Boolean} class
1027      * would match a {@code boolean} primitive.
1028      * </p>
1029      *
1030      * @param cls invoke static method on this class.
1031      * @param methodName get method with this name.
1032      * @param args use these arguments - treat {@code null} as empty array.
1033      * @param parameterTypes match these parameters - treat {@code null} as empty array.
1034      * @return The value returned by the invoked method.
1035      * @throws NoSuchMethodException       Thrown if there is no such accessible method.
1036      * @throws IllegalAccessException      Thrown if this found {@code Method} is enforcing Java language access control and the underlying method is
1037      *                                     inaccessible.
1038      * @throws IllegalArgumentException    Thrown if:
1039      *                                     <ul>
1040      *                                     <li>the found {@code Method} is an instance method and the specified {@code object} argument is not an instance of
1041      *                                     the class or interface declaring the underlying method (or of a subclass or interface implementor);</li>
1042      *                                     <li>the number of actual and formal parameters differ;</li>
1043      *                                     <li>an unwrapping conversion for primitive arguments fails; or</li>
1044      *                                     <li>after possible unwrapping, a parameter value can't be converted to the corresponding formal parameter type by a
1045      *                                     method invocation conversion.</li>
1046      *                                     </ul>
1047      * @throws InvocationTargetException   Thrown if the underlying method throws an exception.
1048      * @throws ExceptionInInitializerError Thrown if the initialization provoked by this method fails.
1049      * @see SecurityManager#checkPermission
1050      */
1051     public static Object invokeStaticMethod(final Class<?> cls, final String methodName, final Object[] args, final Class<?>[] parameterTypes)
1052             throws NoSuchMethodException, IllegalAccessException, InvocationTargetException {
1053         final Class<?>[] paramTypes = ArrayUtils.nullToEmpty(parameterTypes);
1054         final Method method = getMatchingAccessibleMethod(cls, methodName, paramTypes);
1055         requireNonNull(method, cls, methodName, paramTypes);
1056         return method.invoke(null, toVarArgs(method, ArrayUtils.nullToEmpty(args)));
1057     }
1058 
1059     private static Method requireNonNull(final Method method, final Class<?> cls, final String methodName, final Class<?>[] parameterTypes)
1060             throws NoSuchMethodException {
1061         if (method == null) {
1062             throw new NoSuchMethodException(String.format("No method: %s.%s(%s)", ClassUtils.getName(cls), methodName,
1063                     Streams.of(parameterTypes).map(ClassUtils::getName).collect(LangCollectors.joining(", "))));
1064         }
1065         return method;
1066     }
1067 
1068     static Object[] toVarArgs(final Executable executable, final Object[] args)
1069             throws IllegalAccessException, InvocationTargetException, NoSuchMethodException {
1070         return executable.isVarArgs() ? toVarArgs(args, executable.getParameterTypes()) : args;
1071     }
1072 
1073     /**
1074      * Gets an array of arguments in the canonical form, given an arguments array passed to a varargs method, for example an array with the declared number of
1075      * parameters, and whose last parameter is an array of the varargs type.
1076      * <p>
1077      * We follow the <a href="https://docs.oracle.com/javase/specs/jls/se21/html/jls-5.html#jls-5.1.2">JLS 5.1.2. Widening Primitive Conversion</a> rules.
1078      * </p>
1079      *
1080      * @param args                 The array of arguments passed to the varags method.
1081      * @param methodParameterTypes The declared array of method parameter types.
1082      * @return An array of the variadic arguments passed to the method.
1083      * @throws NoSuchMethodException       Thrown if the constructor could not be found.
1084      * @throws IllegalAccessException      Thrown if this {@code Constructor} object is enforcing Java language access control and the underlying constructor is
1085      *                                     inaccessible.
1086      * @throws IllegalArgumentException    Thrown if the number of actual and formal parameters differ; if an unwrapping conversion for primitive arguments
1087      *                                     fails; or if, after possible unwrapping, a parameter value cannot be converted to the corresponding formal parameter
1088      *                                     type by a method invocation conversion; if this constructor pertains to an enum type.
1089      * @throws InstantiationException      Thrown if a class that declares the underlying constructor represents an abstract class.
1090      * @throws InvocationTargetException   Thrown if an underlying constructor throws an exception.
1091      * @throws ExceptionInInitializerError Thrown if an initialization provoked by this method fails.
1092      * @see <a href="https://docs.oracle.com/javase/specs/jls/se21/html/jls-5.html#jls-5.1.2">JLS 5.1.2. Widening Primitive Conversion</a>
1093      */
1094     private static Object[] toVarArgs(final Object[] args, final Class<?>[] methodParameterTypes)
1095             throws IllegalAccessException, InvocationTargetException, NoSuchMethodException {
1096         final int mptLength = methodParameterTypes.length;
1097         if (args.length == mptLength) {
1098             final Object lastArg = args[args.length - 1];
1099             if (lastArg == null || lastArg.getClass().equals(methodParameterTypes[mptLength - 1])) {
1100                 // The args array is already in the canonical form for the method.
1101                 return args;
1102             }
1103         }
1104         // Construct a new array matching the method's declared parameter types.
1105         // Copy the normal (non-varargs) parameters
1106         final Object[] newArgs = ArrayUtils.arraycopy(args, 0, 0, mptLength - 1, () -> new Object[mptLength]);
1107         // Construct a new array for the variadic parameters
1108         final Class<?> varArgComponentType = methodParameterTypes[mptLength - 1].getComponentType();
1109         final Class<?> varArgComponentWrappedType = ClassUtils.primitiveToWrapper(varArgComponentType);
1110         final int varArgLength = args.length - mptLength + 1;
1111         // Copy the variadic arguments into the varargs array, converting types if needed.
1112         Object varArgsArray = Array.newInstance(varArgComponentWrappedType, varArgLength);
1113         final boolean primitiveOrWrapper = ClassUtils.isPrimitiveOrWrapper(varArgComponentWrappedType);
1114         for (int i = 0; i < varArgLength; i++) {
1115             final Object arg = args[mptLength - 1 + i];
1116             try {
1117                 Array.set(varArgsArray, i, primitiveOrWrapper
1118                         ? varArgComponentWrappedType.getConstructor(ClassUtils.wrapperToPrimitive(varArgComponentWrappedType)).newInstance(arg)
1119                         : varArgComponentWrappedType.cast(arg));
1120             } catch (final InstantiationException e) {
1121                 throw new IllegalArgumentException("Cannot convert vararg #" + i, e);
1122             }
1123         }
1124         if (varArgComponentType.isPrimitive()) {
1125             // unbox from wrapper type to primitive type
1126             varArgsArray = ArrayUtils.toPrimitive(varArgsArray);
1127         }
1128         // Store the varargs array in the last position of the array to return
1129         newArgs[mptLength - 1] = varArgsArray;
1130         // Return the canonical varargs array.
1131         return newArgs;
1132     }
1133 
1134     /**
1135      * {@link MethodUtils} instances should NOT be constructed in standard programming. Instead, the class should be used as
1136      * {@code MethodUtils.getAccessibleMethod(method)}.
1137      *
1138      * <p>
1139      * This constructor is {@code public} to permit tools that require a JavaBean instance to operate.
1140      * </p>
1141      *
1142      * @deprecated TODO Make private in 4.0.
1143      */
1144     @Deprecated
1145     public MethodUtils() {
1146         // empty
1147     }
1148 }