001/* 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache License, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * https://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017package org.apache.commons.lang3.reflect; 018 019import java.lang.annotation.Annotation; 020import java.lang.reflect.Array; 021import java.lang.reflect.InvocationTargetException; 022import java.lang.reflect.Method; 023import java.lang.reflect.Type; 024import java.lang.reflect.TypeVariable; 025import java.util.ArrayList; 026import java.util.Arrays; 027import java.util.Collections; 028import java.util.Comparator; 029import java.util.Iterator; 030import java.util.LinkedHashSet; 031import java.util.List; 032import java.util.Map; 033import java.util.Objects; 034import java.util.Set; 035import java.util.TreeMap; 036import java.util.stream.Collectors; 037import java.util.stream.Stream; 038 039import org.apache.commons.lang3.ArrayUtils; 040import org.apache.commons.lang3.ClassUtils; 041import org.apache.commons.lang3.ClassUtils.Interfaces; 042import org.apache.commons.lang3.Validate; 043 044/** 045 * Utility reflection methods focused on {@link Method}s, originally from Commons BeanUtils. 046 * Differences from the BeanUtils version may be noted, especially where similar functionality 047 * already existed within Lang. 048 * 049 * <h2>Known Limitations</h2> 050 * <h3>Accessing Public Methods In A Default Access Superclass</h3> 051 * <p>There is an issue when invoking {@code public} methods contained in a default access superclass on JREs prior to 1.4. 052 * Reflection locates these methods fine and correctly assigns them as {@code public}. 053 * However, an {@link IllegalAccessException} is thrown if the method is invoked.</p> 054 * 055 * <p> 056 * {@link MethodUtils} contains a workaround for this situation. 057 * It will attempt to call {@link java.lang.reflect.AccessibleObject#setAccessible(boolean)} on this method. 058 * If this call succeeds, then the method can be invoked as normal. 059 * This call will only succeed when the application has sufficient security privileges. 060 * If this call fails then the method may fail. 061 * </p> 062 * 063 * @since 2.5 064 */ 065public class MethodUtils { 066 067 private static final Comparator<Method> METHOD_BY_SIGNATURE = Comparator.comparing(Method::toString); 068 069 /** 070 * Computes the aggregate number of inheritance hops between assignable argument class types. Returns -1 071 * if the arguments aren't assignable. Fills a specific purpose for getMatchingMethod and is not generalized. 072 * 073 * @param fromClassArray the Class array to calculate the distance from. 074 * @param toClassArray the Class array to calculate the distance to. 075 * @return the aggregate number of inheritance hops between assignable argument class types. 076 */ 077 private static int distance(final Class<?>[] fromClassArray, final Class<?>[] toClassArray) { 078 int answer = 0; 079 if (!ClassUtils.isAssignable(fromClassArray, toClassArray, true)) { 080 return -1; 081 } 082 for (int offset = 0; offset < fromClassArray.length; offset++) { 083 // Note InheritanceUtils.distance() uses different scoring system. 084 final Class<?> aClass = fromClassArray[offset]; 085 final Class<?> toClass = toClassArray[offset]; 086 if (aClass == null || aClass.equals(toClass)) { 087 continue; 088 } 089 if (ClassUtils.isAssignable(aClass, toClass, true) && !ClassUtils.isAssignable(aClass, toClass, false)) { 090 answer++; 091 } else { 092 answer += 2; 093 } 094 } 095 return answer; 096 } 097 098 /** 099 * 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 100 * {@code null}. This is just a convenience wrapper for {@link #getAccessibleMethod(Method)}. 101 * 102 * @param cls get method from this class. 103 * @param methodName get method with this name. 104 * @param parameterTypes with these parameters types. 105 * @return The accessible method. 106 */ 107 public static Method getAccessibleMethod(final Class<?> cls, final String methodName, final Class<?>... parameterTypes) { 108 return getAccessibleMethod(getMethodObject(cls, methodName, parameterTypes)); 109 } 110 111 /** 112 * Gets an accessible method (that is, one that can be invoked via 113 * reflection) that implements the specified Method. If no such method 114 * can be found, return {@code null}. 115 * 116 * @param method The method that we wish to call, may be null. 117 * @return The accessible method 118 */ 119 public static Method getAccessibleMethod(Method method) { 120 if (!MemberUtils.isAccessible(method)) { 121 return null; 122 } 123 // If the declaring class is public, we are done 124 final Class<?> cls = method.getDeclaringClass(); 125 if (ClassUtils.isPublic(cls)) { 126 return method; 127 } 128 final String methodName = method.getName(); 129 final Class<?>[] parameterTypes = method.getParameterTypes(); 130 // Check the implemented interfaces and subinterfaces 131 method = getAccessibleMethodFromInterfaceNest(cls, methodName, parameterTypes); 132 // Check the superclass chain 133 if (method == null) { 134 method = getAccessibleMethodFromSuperclass(cls, methodName, parameterTypes); 135 } 136 return method; 137 } 138 139 /** 140 * Gets an accessible method (that is, one that can be invoked via 141 * reflection) that implements the specified method, by scanning through 142 * all implemented interfaces and subinterfaces. If no such method 143 * can be found, return {@code null}. 144 * 145 * <p> 146 * There isn't any good reason why this method must be {@code private}. 147 * It is because there doesn't seem any reason why other classes should 148 * call this rather than the higher level methods. 149 * </p> 150 * 151 * @param cls Parent class for the interfaces to be checked 152 * @param methodName Method name of the method we wish to call 153 * @param parameterTypes The parameter type signatures 154 * @return the accessible method or {@code null} if not found 155 */ 156 private static Method getAccessibleMethodFromInterfaceNest(Class<?> cls, final String methodName, final Class<?>... parameterTypes) { 157 // Search up the superclass chain 158 for (; cls != null; cls = cls.getSuperclass()) { 159 // Check the implemented interfaces of the parent class 160 final Class<?>[] interfaces = cls.getInterfaces(); 161 for (final Class<?> anInterface : interfaces) { 162 // Is this interface public? 163 if (!ClassUtils.isPublic(anInterface)) { 164 continue; 165 } 166 // Does the method exist on this interface? 167 try { 168 return anInterface.getDeclaredMethod(methodName, parameterTypes); 169 } catch (final NoSuchMethodException ignored) { 170 /* 171 * Swallow, if no method is found after the loop then this method returns null. 172 */ 173 } 174 // Recursively check our parent interfaces 175 final Method method = getAccessibleMethodFromInterfaceNest(anInterface, methodName, parameterTypes); 176 if (method != null) { 177 return method; 178 } 179 } 180 } 181 return null; 182 } 183 184 /** 185 * Gets an accessible method (that is, one that can be invoked via 186 * reflection) by scanning through the superclasses. If no such method 187 * can be found, return {@code null}. 188 * 189 * @param cls Class to be checked. 190 * @param methodName Method name of the method we wish to call. 191 * @param parameterTypes The parameter type signatures. 192 * @return the accessible method or {@code null} if not found. 193 */ 194 private static Method getAccessibleMethodFromSuperclass(final Class<?> cls, final String methodName, final Class<?>... parameterTypes) { 195 Class<?> parentClass = cls.getSuperclass(); 196 while (parentClass != null) { 197 if (ClassUtils.isPublic(parentClass)) { 198 return getMethodObject(parentClass, methodName, parameterTypes); 199 } 200 parentClass = parentClass.getSuperclass(); 201 } 202 return null; 203 } 204 205 /** 206 * Gets a combination of {@link ClassUtils#getAllSuperclasses(Class)} and 207 * {@link ClassUtils#getAllInterfaces(Class)}, one from superclasses, one 208 * from interfaces, and so on in a breadth first way. 209 * 210 * @param cls the class to look up, may be {@code null} 211 * @return the combined {@link List} of superclasses and interfaces in order 212 * going up from this one 213 * {@code null} if null input 214 */ 215 private static List<Class<?>> getAllSuperclassesAndInterfaces(final Class<?> cls) { 216 if (cls == null) { 217 return null; 218 } 219 final List<Class<?>> allSuperClassesAndInterfaces = new ArrayList<>(); 220 final List<Class<?>> allSuperclasses = ClassUtils.getAllSuperclasses(cls); 221 int superClassIndex = 0; 222 final List<Class<?>> allInterfaces = ClassUtils.getAllInterfaces(cls); 223 int interfaceIndex = 0; 224 while (interfaceIndex < allInterfaces.size() || superClassIndex < allSuperclasses.size()) { 225 final Class<?> acls; 226 if (interfaceIndex >= allInterfaces.size() || superClassIndex < allSuperclasses.size() && superClassIndex < interfaceIndex) { 227 acls = allSuperclasses.get(superClassIndex++); 228 } else { 229 acls = allInterfaces.get(interfaceIndex++); 230 } 231 allSuperClassesAndInterfaces.add(acls); 232 } 233 return allSuperClassesAndInterfaces; 234 } 235 236 /** 237 * Gets the annotation object with the given annotation type that is present on the given method 238 * or optionally on any equivalent method in super classes and interfaces. Returns null if the annotation 239 * type was not present. 240 * 241 * <p> 242 * Stops searching for an annotation once the first annotation of the specified type has been 243 * found. Additional annotations of the specified type will be silently ignored. 244 * </p> 245 * 246 * @param <A> 247 * the annotation type 248 * @param method 249 * the {@link Method} to query, may be null. 250 * @param annotationCls 251 * the {@link Annotation} to check if is present on the method 252 * @param searchSupers 253 * determines if a lookup in the entire inheritance hierarchy of the given class is performed 254 * if the annotation was not directly present 255 * @param ignoreAccess 256 * determines if underlying method has to be accessible 257 * @return the first matching annotation, or {@code null} if not found 258 * @throws NullPointerException if either the method or annotation class is {@code null} 259 * @throws SecurityException if an underlying accessible object's method denies the request. 260 * @see SecurityManager#checkPermission 261 * @since 3.6 262 */ 263 public static <A extends Annotation> A getAnnotation(final Method method, final Class<A> annotationCls, final boolean searchSupers, 264 final boolean ignoreAccess) { 265 Objects.requireNonNull(method, "method"); 266 Objects.requireNonNull(annotationCls, "annotationCls"); 267 if (!ignoreAccess && !MemberUtils.isAccessible(method)) { 268 return null; 269 } 270 A annotation = method.getAnnotation(annotationCls); 271 if (annotation == null && searchSupers) { 272 final Class<?> mcls = method.getDeclaringClass(); 273 final List<Class<?>> classes = getAllSuperclassesAndInterfaces(mcls); 274 for (final Class<?> acls : classes) { 275 final Method equivalentMethod = ignoreAccess ? getMatchingMethod(acls, method.getName(), method.getParameterTypes()) 276 : getMatchingAccessibleMethod(acls, method.getName(), method.getParameterTypes()); 277 if (equivalentMethod != null) { 278 annotation = equivalentMethod.getAnnotation(annotationCls); 279 if (annotation != null) { 280 break; 281 } 282 } 283 } 284 } 285 return annotation; 286 } 287 288 /** 289 * Gets an accessible method that matches the given name and has compatible parameters. Compatible parameters mean that every method parameter is assignable 290 * from the given parameters. In other words, it finds a method with the given name that will take the parameters given. 291 * 292 * <p> 293 * This method is used by {@link #invokeMethod(Object object, String methodName, Object[] args, Class[] parameterTypes)}. 294 * </p> 295 * <p> 296 * This method can match primitive parameter by passing in wrapper classes. For example, a {@link Boolean} will match a primitive {@code boolean} parameter. 297 * </p> 298 * 299 * @param cls find method in this class. 300 * @param methodName find method with this name. 301 * @param parameterTypes find method with most compatible parameters. 302 * @return The accessible method or null. 303 * @throws SecurityException if an underlying accessible object's method denies the request. 304 * @see SecurityManager#checkPermission 305 */ 306 public static Method getMatchingAccessibleMethod(final Class<?> cls, final String methodName, final Class<?>... parameterTypes) { 307 final Method candidate = getMethodObject(cls, methodName, parameterTypes); 308 if (candidate != null) { 309 return MemberUtils.setAccessibleWorkaround(candidate); 310 } 311 // search through all methods 312 final Method[] methods = cls.getMethods(); 313 final List<Method> matchingMethods = Stream.of(methods) 314 .filter(method -> method.getName().equals(methodName) && MemberUtils.isMatchingMethod(method, parameterTypes)).collect(Collectors.toList()); 315 // Sort methods by signature to force deterministic result 316 matchingMethods.sort(METHOD_BY_SIGNATURE); 317 Method bestMatch = null; 318 for (final Method method : matchingMethods) { 319 // get accessible version of method 320 final Method accessibleMethod = getAccessibleMethod(method); 321 if (accessibleMethod != null && (bestMatch == null || MemberUtils.compareMethodFit(accessibleMethod, bestMatch, parameterTypes) < 0)) { 322 bestMatch = accessibleMethod; 323 } 324 } 325 if (bestMatch != null) { 326 MemberUtils.setAccessibleWorkaround(bestMatch); 327 } 328 if (bestMatch != null && bestMatch.isVarArgs() && bestMatch.getParameterTypes().length > 0 && parameterTypes.length > 0) { 329 final Class<?>[] methodParameterTypes = bestMatch.getParameterTypes(); 330 final Class<?> methodParameterComponentType = methodParameterTypes[methodParameterTypes.length - 1].getComponentType(); 331 final String methodParameterComponentTypeName = ClassUtils.primitiveToWrapper(methodParameterComponentType).getName(); 332 final Class<?> lastParameterType = parameterTypes[parameterTypes.length - 1]; 333 final String parameterTypeName = lastParameterType == null ? null : lastParameterType.getName(); 334 final String parameterTypeSuperClassName = lastParameterType == null ? null 335 : lastParameterType.getSuperclass() != null ? lastParameterType.getSuperclass().getName() : null; 336 if (parameterTypeName != null && parameterTypeSuperClassName != null && !methodParameterComponentTypeName.equals(parameterTypeName) 337 && !methodParameterComponentTypeName.equals(parameterTypeSuperClassName)) { 338 return null; 339 } 340 } 341 return bestMatch; 342 } 343 344 /** 345 * Gets a method whether or not it's accessible. If no such method 346 * can be found, return {@code null}. 347 * 348 * @param cls The class that will be subjected to the method search 349 * @param methodName The method that we wish to call 350 * @param parameterTypes Argument class types 351 * @throws IllegalStateException if there is no unique result 352 * @throws NullPointerException if the class is {@code null} 353 * @return The method 354 * @since 3.5 355 */ 356 public static Method getMatchingMethod(final Class<?> cls, final String methodName, final Class<?>... parameterTypes) { 357 Objects.requireNonNull(cls, "cls"); 358 Validate.notEmpty(methodName, "methodName"); 359 final List<Method> methods = Stream.of(cls.getDeclaredMethods()) 360 .filter(method -> method.getName().equals(methodName)) 361 .collect(Collectors.toList()); 362 final List<Class<?>> allSuperclassesAndInterfaces = getAllSuperclassesAndInterfaces(cls); 363 Collections.reverse(allSuperclassesAndInterfaces); 364 allSuperclassesAndInterfaces.stream() 365 .map(Class::getDeclaredMethods) 366 .flatMap(Stream::of) 367 .filter(method -> method.getName().equals(methodName)) 368 .forEach(methods::add); 369 for (final Method method : methods) { 370 if (Arrays.deepEquals(method.getParameterTypes(), parameterTypes)) { 371 return method; 372 } 373 } 374 final TreeMap<Integer, List<Method>> candidates = new TreeMap<>(); 375 methods.stream() 376 .filter(method -> ClassUtils.isAssignable(parameterTypes, method.getParameterTypes(), true)) 377 .forEach(method -> { 378 final int distance = distance(parameterTypes, method.getParameterTypes()); 379 final List<Method> candidatesAtDistance = candidates.computeIfAbsent(distance, k -> new ArrayList<>()); 380 candidatesAtDistance.add(method); 381 }); 382 if (candidates.isEmpty()) { 383 return null; 384 } 385 final List<Method> bestCandidates = candidates.values().iterator().next(); 386 if (bestCandidates.size() == 1 || !Objects.equals(bestCandidates.get(0).getDeclaringClass(), 387 bestCandidates.get(1).getDeclaringClass())) { 388 return bestCandidates.get(0); 389 } 390 throw new IllegalStateException(String.format("Found multiple candidates for method %s on class %s : %s", 391 methodName + Stream.of(parameterTypes).map(String::valueOf).collect(Collectors.joining(",", "(", ")")), cls.getName(), 392 bestCandidates.stream().map(Method::toString).collect(Collectors.joining(",", "[", "]")))); 393 } 394 395 /** 396 * Gets a Method, or {@code null} if a documented {@link Class#getMethod(String, Class...) } exception is thrown. 397 * 398 * @param cls Receiver for {@link Class#getMethod(String, Class...)}. 399 * @param name the name of the method. 400 * @param parameterTypes the list of parameters. 401 * @return a Method or {@code null}. 402 * @see SecurityManager#checkPermission 403 * @see Class#getMethod(String, Class...) 404 * @since 3.15.0 405 */ 406 public static Method getMethodObject(final Class<?> cls, final String name, final Class<?>... parameterTypes) { 407 try { 408 return name != null && cls != null ? cls.getMethod(name, parameterTypes) : null; 409 } catch (final NoSuchMethodException | SecurityException e) { 410 return null; 411 } 412 } 413 414 /** 415 * Gets all class level public methods of the given class that are annotated with the given annotation. 416 * @param cls 417 * the {@link Class} to query 418 * @param annotationCls 419 * the {@link Annotation} that must be present on a method to be matched 420 * @return a list of Methods (possibly empty). 421 * @throws NullPointerException 422 * if the class or annotation are {@code null} 423 * @since 3.4 424 */ 425 public static List<Method> getMethodsListWithAnnotation(final Class<?> cls, final Class<? extends Annotation> annotationCls) { 426 return getMethodsListWithAnnotation(cls, annotationCls, false, false); 427 } 428 429 /** 430 * Gets all methods of the given class that are annotated with the given annotation. 431 * 432 * @param cls 433 * the {@link Class} to query 434 * @param annotationCls 435 * the {@link Annotation} that must be present on a method to be matched 436 * @param searchSupers 437 * determines if a lookup in the entire inheritance hierarchy of the given class should be performed 438 * @param ignoreAccess 439 * determines if non-public methods should be considered 440 * @return a list of Methods (possibly empty). 441 * @throws NullPointerException if either the class or annotation class is {@code null} 442 * @since 3.6 443 */ 444 public static List<Method> getMethodsListWithAnnotation(final Class<?> cls, final Class<? extends Annotation> annotationCls, final boolean searchSupers, 445 final boolean ignoreAccess) { 446 Objects.requireNonNull(cls, "cls"); 447 Objects.requireNonNull(annotationCls, "annotationCls"); 448 final List<Class<?>> classes = searchSupers ? getAllSuperclassesAndInterfaces(cls) : new ArrayList<>(); 449 classes.add(0, cls); 450 final List<Method> annotatedMethods = new ArrayList<>(); 451 classes.forEach(acls -> { 452 final Method[] methods = ignoreAccess ? acls.getDeclaredMethods() : acls.getMethods(); 453 Stream.of(methods).filter(method -> method.isAnnotationPresent(annotationCls)).forEachOrdered(annotatedMethods::add); 454 }); 455 return annotatedMethods; 456 } 457 458 /** 459 * Gets all class level public methods of the given class that are annotated with the given annotation. 460 * 461 * @param cls 462 * the {@link Class} to query 463 * @param annotationCls 464 * the {@link java.lang.annotation.Annotation} that must be present on a method to be matched 465 * @return an array of Methods (possibly empty). 466 * @throws NullPointerException if the class or annotation are {@code null} 467 * @since 3.4 468 */ 469 public static Method[] getMethodsWithAnnotation(final Class<?> cls, final Class<? extends Annotation> annotationCls) { 470 return getMethodsWithAnnotation(cls, annotationCls, false, false); 471 } 472 473 /** 474 * Gets all methods of the given class that are annotated with the given annotation. 475 * 476 * @param cls 477 * the {@link Class} to query 478 * @param annotationCls 479 * the {@link java.lang.annotation.Annotation} that must be present on a method to be matched 480 * @param searchSupers 481 * determines if a lookup in the entire inheritance hierarchy of the given class should be performed 482 * @param ignoreAccess 483 * determines if non-public methods should be considered 484 * @return an array of Methods (possibly empty). 485 * @throws NullPointerException if the class or annotation are {@code null} 486 * @since 3.6 487 */ 488 public static Method[] getMethodsWithAnnotation(final Class<?> cls, final Class<? extends Annotation> annotationCls, final boolean searchSupers, 489 final boolean ignoreAccess) { 490 return getMethodsListWithAnnotation(cls, annotationCls, searchSupers, ignoreAccess).toArray(ArrayUtils.EMPTY_METHOD_ARRAY); 491 } 492 493 /** 494 * Gets the hierarchy of overridden methods down to {@code result} respecting generics. 495 * 496 * @param method lowest to consider 497 * @param interfacesBehavior whether to search interfaces, {@code null} {@code implies} false 498 * @return a {@code Set<Method>} in ascending order from subclass to superclass 499 * @throws NullPointerException if the specified method is {@code null} 500 * @throws SecurityException if an underlying accessible object's method denies the request. 501 * @see SecurityManager#checkPermission 502 * @since 3.2 503 */ 504 public static Set<Method> getOverrideHierarchy(final Method method, final Interfaces interfacesBehavior) { 505 Objects.requireNonNull(method, "method"); 506 final Set<Method> result = new LinkedHashSet<>(); 507 result.add(method); 508 final Class<?>[] parameterTypes = method.getParameterTypes(); 509 final Class<?> declaringClass = method.getDeclaringClass(); 510 final Iterator<Class<?>> hierarchy = ClassUtils.hierarchy(declaringClass, interfacesBehavior).iterator(); 511 //skip the declaring class :P 512 hierarchy.next(); 513 hierarchyTraversal: while (hierarchy.hasNext()) { 514 final Class<?> c = hierarchy.next(); 515 final Method m = getMatchingAccessibleMethod(c, method.getName(), parameterTypes); 516 if (m == null) { 517 continue; 518 } 519 if (Arrays.equals(m.getParameterTypes(), parameterTypes)) { 520 // matches without generics 521 result.add(m); 522 continue; 523 } 524 // necessary to get arguments every time in the case that we are including interfaces 525 final Map<TypeVariable<?>, Type> typeArguments = TypeUtils.getTypeArguments(declaringClass, m.getDeclaringClass()); 526 for (int i = 0; i < parameterTypes.length; i++) { 527 final Type childType = TypeUtils.unrollVariables(typeArguments, method.getGenericParameterTypes()[i]); 528 final Type parentType = TypeUtils.unrollVariables(typeArguments, m.getGenericParameterTypes()[i]); 529 if (!TypeUtils.equals(childType, parentType)) { 530 continue hierarchyTraversal; 531 } 532 } 533 result.add(m); 534 } 535 return result; 536 } 537 538 /** 539 * Gets an array of arguments in the canonical form, given an arguments array passed to a varargs method, 540 * for example an array with the declared number of parameters, and whose last parameter is an array of the varargs type. 541 * 542 * @param args the array of arguments passed to the varags method 543 * @param methodParameterTypes the declared array of method parameter types 544 * @return an array of the variadic arguments passed to the method 545 * @since 3.5 546 */ 547 static Object[] getVarArgs(final Object[] args, final Class<?>[] methodParameterTypes) { 548 final int mptLength = methodParameterTypes.length; 549 if (args.length == mptLength) { 550 final Object lastArg = args[args.length - 1]; 551 if (lastArg == null || lastArg.getClass().equals(methodParameterTypes[mptLength - 1])) { 552 // The args array is already in the canonical form for the method. 553 return args; 554 } 555 } 556 // Construct a new array matching the method's declared parameter types. 557 // Copy the normal (non-varargs) parameters 558 final Object[] newArgs = ArrayUtils.arraycopy(args, 0, 0, mptLength - 1, () -> new Object[mptLength]); 559 // Construct a new array for the variadic parameters 560 final Class<?> varArgComponentType = methodParameterTypes[mptLength - 1].getComponentType(); 561 final int varArgLength = args.length - mptLength + 1; 562 // Copy the variadic arguments into the varargs array. 563 Object varArgsArray = ArrayUtils.arraycopy(args, mptLength - 1, 0, varArgLength, 564 s -> Array.newInstance(ClassUtils.primitiveToWrapper(varArgComponentType), varArgLength)); 565 if (varArgComponentType.isPrimitive()) { 566 // unbox from wrapper type to primitive type 567 varArgsArray = ArrayUtils.toPrimitive(varArgsArray); 568 } 569 // Store the varargs array in the last position of the array to return 570 newArgs[mptLength - 1] = varArgsArray; 571 // Return the canonical varargs array. 572 return newArgs; 573 } 574 575 /** 576 * Invokes a method whose parameter types match exactly the object type. 577 * 578 * <p> 579 * This uses reflection to invoke the method obtained from a call to {@link #getAccessibleMethod(Class, String, Class[])}. 580 * </p> 581 * 582 * @param object invoke method on this object. 583 * @param methodName get method with this name. 584 * @return The value returned by the invoked method. 585 * @throws NoSuchMethodException Thrown if there is no such accessible method. 586 * @throws IllegalAccessException Thrown if this found {@code Method} is enforcing Java language access control and the underlying method is 587 * inaccessible. 588 * @throws IllegalArgumentException Thrown if: 589 * <ul> 590 * <li>the found {@code Method} is an instance method and the specified {@code object} argument is not an instance of 591 * the class or interface declaring the underlying method (or of a subclass or interface implementor);</li> 592 * <li>the number of actual and formal parameters differ;</li> 593 * </ul> 594 * @throws InvocationTargetException Thrown if the underlying method throws an exception. 595 * @throws NullPointerException Thrown if the specified {@code object} is null. 596 * @throws ExceptionInInitializerError Thrown if the initialization provoked by this method fails. 597 * @since 3.4 598 */ 599 public static Object invokeExactMethod(final Object object, final String methodName) 600 throws NoSuchMethodException, IllegalAccessException, InvocationTargetException { 601 return invokeExactMethod(object, methodName, ArrayUtils.EMPTY_OBJECT_ARRAY, null); 602 } 603 604 /** 605 * Invokes a method whose parameter types match exactly the object types. 606 * 607 * <p> 608 * This uses reflection to invoke the method obtained from a call to {@link #getAccessibleMethod(Class, String, Class[])}. 609 * </p> 610 * 611 * @param object invoke method on this object. 612 * @param methodName get method with this name. 613 * @param args use these arguments - treat null as empty array. 614 * @return The value returned by the invoked method. 615 * @throws NoSuchMethodException Thrown if there is no such accessible method. 616 * @throws IllegalAccessException Thrown if this found {@code Method} is enforcing Java language access control and the underlying method is 617 * inaccessible. 618 * @throws IllegalArgumentException Thrown if: 619 * <ul> 620 * <li>the found {@code Method} is an instance method and the specified {@code object} argument is not an instance of 621 * the class or interface declaring the underlying method (or of a subclass or interface implementor);</li> 622 * <li>the number of actual and formal parameters differ;</li> 623 * <li>an unwrapping conversion for primitive arguments fails; or</li> 624 * <li>after possible unwrapping, a parameter value can't be converted to the corresponding formal parameter type by a 625 * method invocation conversion.</li> 626 * </ul> 627 * @throws InvocationTargetException Thrown if the underlying method throws an exception. 628 * @throws NullPointerException Thrown if the specified {@code object} is null. 629 * @throws ExceptionInInitializerError Thrown if the initialization provoked by this method fails. 630 */ 631 public static Object invokeExactMethod(final Object object, final String methodName, final Object... args) 632 throws NoSuchMethodException, IllegalAccessException, InvocationTargetException { 633 final Object[] actuals = ArrayUtils.nullToEmpty(args); 634 return invokeExactMethod(object, methodName, actuals, ClassUtils.toClass(actuals)); 635 } 636 637 /** 638 * Invokes a method whose parameter types match exactly the parameter types given. 639 * 640 * <p> 641 * This uses reflection to invoke the method obtained from a call to {@link #getAccessibleMethod(Class, String, Class[])}. 642 * </p> 643 * 644 * @param object Invokes a method on this object. 645 * @param methodName Gets a method with this name. 646 * @param args Method arguments - treat null as empty array. 647 * @param parameterTypes Match these parameters - treat {@code null} as empty array. 648 * @return The value returned by the invoked method. 649 * @throws NoSuchMethodException Thrown if there is no such accessible method. 650 * @throws IllegalAccessException Thrown if this found {@code Method} is enforcing Java language access control and the underlying method is 651 * inaccessible. 652 * @throws IllegalArgumentException Thrown if: 653 * <ul> 654 * <li>the found {@code Method} is an instance method and the specified {@code object} argument is not an instance of 655 * the class or interface declaring the underlying method (or of a subclass or interface implementor);</li> 656 * <li>the number of actual and formal parameters differ;</li> 657 * <li>an unwrapping conversion for primitive arguments fails; or</li> 658 * <li>after possible unwrapping, a parameter value can't be converted to the corresponding formal parameter type by a 659 * method invocation conversion.</li> 660 * </ul> 661 * @throws InvocationTargetException Thrown if the underlying method throws an exception. 662 * @throws NullPointerException Thrown if the specified {@code object} is null. 663 * @throws ExceptionInInitializerError Thrown if the initialization provoked by this method fails. 664 */ 665 public static Object invokeExactMethod(final Object object, final String methodName, final Object[] args, final Class<?>[] parameterTypes) 666 throws NoSuchMethodException, IllegalAccessException, InvocationTargetException { 667 final Class<?> cls = Objects.requireNonNull(object, "object").getClass(); 668 final Method method = getAccessibleMethod(cls, methodName, ArrayUtils.nullToEmpty(parameterTypes)); 669 if (method == null) { 670 throw new NoSuchMethodException("No such accessible method: " + methodName + "() on object: " + cls.getName()); 671 } 672 return method.invoke(object, ArrayUtils.nullToEmpty(args)); 673 } 674 675 /** 676 * Invokes a {@code static} method whose parameter types match exactly the object types. 677 * 678 * <p> 679 * This uses reflection to invoke the method obtained from a call to {@link #getAccessibleMethod(Class, String, Class[])}. 680 * </p> 681 * 682 * @param cls invoke static method on this class 683 * @param methodName get method with this name 684 * @param args use these arguments - treat {@code null} as empty array 685 * @return The value returned by the invoked method 686 * @throws NoSuchMethodException Thrown if there is no such accessible method. 687 * @throws IllegalAccessException Thrown if this found {@code Method} is enforcing Java language access control and the underlying method is 688 * inaccessible. 689 * @throws IllegalArgumentException Thrown if: 690 * <ul> 691 * <li>the found {@code Method} is an instance method and the specified {@code object} argument is not an instance of 692 * the class or interface declaring the underlying method (or of a subclass or interface implementor);</li> 693 * <li>the number of actual and formal parameters differ;</li> 694 * <li>an unwrapping conversion for primitive arguments fails; or</li> 695 * <li>after possible unwrapping, a parameter value can't be converted to the corresponding formal parameter type by a 696 * method invocation conversion.</li> 697 * </ul> 698 * @throws InvocationTargetException Thrown if the underlying method throws an exception. 699 * @throws ExceptionInInitializerError Thrown if the initialization provoked by this method fails. 700 */ 701 public static Object invokeExactStaticMethod(final Class<?> cls, final String methodName, final Object... args) 702 throws NoSuchMethodException, IllegalAccessException, InvocationTargetException { 703 final Object[] actuals = ArrayUtils.nullToEmpty(args); 704 return invokeExactStaticMethod(cls, methodName, actuals, ClassUtils.toClass(actuals)); 705 } 706 707 /** 708 * Invokes a {@code static} method whose parameter types match exactly the parameter types given. 709 * 710 * <p> 711 * This uses reflection to invoke the method obtained from a call to {@link #getAccessibleMethod(Class, String, Class[])}. 712 * </p> 713 * 714 * @param cls invoke static method on this class 715 * @param methodName get method with this name 716 * @param args use these arguments - treat {@code null} as empty array 717 * @param parameterTypes match these parameters - treat {@code null} as empty array 718 * @return The value returned by the invoked method 719 * @throws NoSuchMethodException Thrown if there is no such accessible method. 720 * @throws IllegalAccessException Thrown if this found {@code Method} is enforcing Java language access control and the underlying method is 721 * inaccessible. 722 * @throws IllegalArgumentException Thrown if: 723 * <ul> 724 * <li>the found {@code Method} is an instance method and the specified {@code object} argument is not an instance of 725 * the class or interface declaring the underlying method (or of a subclass or interface implementor);</li> 726 * <li>the number of actual and formal parameters differ;</li> 727 * <li>an unwrapping conversion for primitive arguments fails; or</li> 728 * <li>after possible unwrapping, a parameter value can't be converted to the corresponding formal parameter type by a 729 * method invocation conversion.</li> 730 * </ul> 731 * @throws InvocationTargetException Thrown if the underlying method throws an exception. 732 * @throws ExceptionInInitializerError Thrown if the initialization provoked by this method fails. 733 */ 734 public static Object invokeExactStaticMethod(final Class<?> cls, final String methodName, final Object[] args, final Class<?>[] parameterTypes) 735 throws NoSuchMethodException, IllegalAccessException, InvocationTargetException { 736 final Method method = getAccessibleMethod(cls, methodName, ArrayUtils.nullToEmpty(parameterTypes)); 737 if (method == null) { 738 throw new NoSuchMethodException("No such accessible method: " + methodName + "() on class: " + cls.getName()); 739 } 740 return method.invoke(null, ArrayUtils.nullToEmpty(args)); 741 } 742 743 /** 744 * Invokes a named method without parameters. 745 * 746 * <p> 747 * This is a convenient wrapper for 748 * {@link #invokeMethod(Object object, boolean forceAccess, String methodName, Object[] args, Class[] parameterTypes)}. 749 * </p> 750 * 751 * @param object invoke method on this object 752 * @param forceAccess force access to invoke method even if it's not accessible 753 * @param methodName get method with this name 754 * @return The value returned by the invoked method 755 * @throws NoSuchMethodException Thrown if there is no such accessible method. 756 * @throws IllegalAccessException Thrown if this found {@code Method} is enforcing Java language access control and the underlying method is 757 * inaccessible. 758 * @throws IllegalArgumentException Thrown if: 759 * <ul> 760 * <li>the found {@code Method} is an instance method and the specified {@code object} argument is not an instance of 761 * the class or interface declaring the underlying method (or of a subclass or interface implementor);</li> 762 * <li>the number of actual and formal parameters differ;</li> 763 * <li>an unwrapping conversion for primitive arguments fails; or</li> 764 * <li>after possible unwrapping, a parameter value can't be converted to the corresponding formal parameter type by a 765 * method invocation conversion.</li> 766 * </ul> 767 * @throws InvocationTargetException Thrown if the underlying method throws an exception. 768 * @throws NullPointerException Thrown if the specified {@code object} is null. 769 * @throws ExceptionInInitializerError Thrown if the initialization provoked by this method fails. 770 * @see SecurityManager#checkPermission 771 * @since 3.5 772 */ 773 public static Object invokeMethod(final Object object, final boolean forceAccess, final String methodName) 774 throws NoSuchMethodException, IllegalAccessException, InvocationTargetException { 775 return invokeMethod(object, forceAccess, methodName, ArrayUtils.EMPTY_OBJECT_ARRAY, null); 776 } 777 778 /** 779 * Invokes a named method whose parameter type matches the object type. 780 * 781 * <p> 782 * This method supports calls to methods taking primitive parameters 783 * via passing in wrapping classes. So, for example, a {@link Boolean} object 784 * would match a {@code boolean} primitive. 785 * </p> 786 * <p> 787 * This is a convenient wrapper for 788 * {@link #invokeMethod(Object object, boolean forceAccess, String methodName, Object[] args, Class[] parameterTypes)}. 789 * </p> 790 * 791 * @param object invoke method on this object 792 * @param forceAccess force access to invoke method even if it's not accessible 793 * @param methodName get method with this name 794 * @param args use these arguments - treat null as empty array 795 * @return The value returned by the invoked method 796 * @throws NoSuchMethodException Thrown if there is no such accessible method. 797 * @throws IllegalAccessException Thrown if this found {@code Method} is enforcing Java language access control and the underlying method is 798 * inaccessible. 799 * @throws IllegalArgumentException Thrown if: 800 * <ul> 801 * <li>the found {@code Method} is an instance method and the specified {@code object} argument is not an instance of 802 * the class or interface declaring the underlying method (or of a subclass or interface implementor);</li> 803 * <li>the number of actual and formal parameters differ;</li> 804 * <li>an unwrapping conversion for primitive arguments fails; or</li> 805 * <li>after possible unwrapping, a parameter value can't be converted to the corresponding formal parameter type by a 806 * method invocation conversion.</li> 807 * </ul> 808 * @throws InvocationTargetException Thrown if the underlying method throws an exception. 809 * @throws NullPointerException Thrown if the specified {@code object} is null. 810 * @throws ExceptionInInitializerError Thrown if the initialization provoked by this method fails. 811 * @see SecurityManager#checkPermission 812 * @since 3.5 813 */ 814 public static Object invokeMethod(final Object object, final boolean forceAccess, final String methodName, final Object... args) 815 throws NoSuchMethodException, IllegalAccessException, InvocationTargetException { 816 final Object[] actuals = ArrayUtils.nullToEmpty(args); 817 return invokeMethod(object, forceAccess, methodName, actuals, ClassUtils.toClass(actuals)); 818 } 819 820 /** 821 * Invokes a named method whose parameter type matches the object type. 822 * 823 * <p> 824 * This method supports calls to methods taking primitive parameters 825 * via passing in wrapping classes. So, for example, a {@link Boolean} object 826 * would match a {@code boolean} primitive. 827 * </p> 828 * 829 * @param object invoke method on this object 830 * @param forceAccess force access to invoke method even if it's not accessible 831 * @param methodName get method with this name 832 * @param args use these arguments - treat null as empty array 833 * @param parameterTypes match these parameters - treat null as empty array 834 * @return The value returned by the invoked method 835 * @throws NoSuchMethodException Thrown if there is no such accessible method. 836 * @throws IllegalAccessException Thrown if this found {@code Method} is enforcing Java language access control and the underlying method is 837 * inaccessible. 838 * @throws IllegalArgumentException Thrown if: 839 * <ul> 840 * <li>the found {@code Method} is an instance method and the specified {@code object} argument is not an instance of 841 * the class or interface declaring the underlying method (or of a subclass or interface implementor);</li> 842 * <li>the number of actual and formal parameters differ;</li> 843 * <li>an unwrapping conversion for primitive arguments fails; or</li> 844 * <li>after possible unwrapping, a parameter value can't be converted to the corresponding formal parameter type by a 845 * method invocation conversion.</li> 846 * </ul> 847 * @throws InvocationTargetException Thrown if the underlying method throws an exception. 848 * @throws NullPointerException Thrown if the specified {@code object} is null. 849 * @throws ExceptionInInitializerError Thrown if the initialization provoked by this method fails. 850 * @see SecurityManager#checkPermission 851 * @since 3.5 852 */ 853 public static Object invokeMethod(final Object object, final boolean forceAccess, final String methodName, final Object[] args, Class<?>[] parameterTypes) 854 throws NoSuchMethodException, IllegalAccessException, InvocationTargetException { 855 Objects.requireNonNull(object, "object"); 856 parameterTypes = ArrayUtils.nullToEmpty(parameterTypes); 857 final String messagePrefix; 858 final Method method; 859 final Class<? extends Object> cls = object.getClass(); 860 if (forceAccess) { 861 messagePrefix = "No such method: "; 862 method = getMatchingMethod(cls, methodName, parameterTypes); 863 if (method != null && !method.isAccessible()) { 864 method.setAccessible(true); 865 } 866 } else { 867 messagePrefix = "No such accessible method: "; 868 method = getMatchingAccessibleMethod(cls, methodName, parameterTypes); 869 } 870 if (method == null) { 871 throw new NoSuchMethodException(messagePrefix + methodName + "() on object: " + cls.getName()); 872 } 873 return method.invoke(object, toVarArgs(method, ArrayUtils.nullToEmpty(args))); 874 } 875 876 /** 877 * Invokes a named method without parameters. 878 * 879 * <p> 880 * This method delegates the method search to {@link #getMatchingAccessibleMethod(Class, String, Class[])}. 881 * </p> 882 * <p> 883 * This is a convenient wrapper for 884 * {@link #invokeMethod(Object object, String methodName, Object[] args, Class[] parameterTypes)}. 885 * </p> 886 * 887 * @param object invoke method on this object 888 * @param methodName get method with this name 889 * @return The value returned by the invoked method 890 * @throws NoSuchMethodException if there is no such accessible method 891 * @throws InvocationTargetException wraps an exception thrown by the method invoked 892 * @throws IllegalAccessException if the requested method is not accessible via reflection 893 * @throws SecurityException if an underlying accessible object's method denies the request. 894 * @see SecurityManager#checkPermission 895 * @since 3.4 896 */ 897 public static Object invokeMethod(final Object object, final String methodName) throws NoSuchMethodException, 898 IllegalAccessException, InvocationTargetException { 899 return invokeMethod(object, methodName, ArrayUtils.EMPTY_OBJECT_ARRAY, null); 900 } 901 902 /** 903 * Invokes a named method whose parameter type matches the object type. 904 * 905 * <p> 906 * This method delegates the method search to {@link #getMatchingAccessibleMethod(Class, String, Class[])}. 907 * </p> 908 * <p> 909 * This method supports calls to methods taking primitive parameters 910 * via passing in wrapping classes. So, for example, a {@link Boolean} object 911 * would match a {@code boolean} primitive. 912 * </p> 913 * <p> 914 * This is a convenient wrapper for 915 * {@link #invokeMethod(Object object, String methodName, Object[] args, Class[] parameterTypes)}. 916 * </p> 917 * 918 * @param object invoke method on this object 919 * @param methodName get method with this name 920 * @param args use these arguments - treat null as empty array 921 * @return The value returned by the invoked method 922 * @throws NoSuchMethodException if there is no such accessible method 923 * @throws InvocationTargetException wraps an exception thrown by the method invoked 924 * @throws IllegalAccessException if the requested method is not accessible via reflection 925 * @throws NullPointerException if the object or method name are {@code null} 926 * @throws SecurityException if an underlying accessible object's method denies the request. 927 * @see SecurityManager#checkPermission 928 */ 929 public static Object invokeMethod(final Object object, final String methodName, final Object... args) 930 throws NoSuchMethodException, IllegalAccessException, InvocationTargetException { 931 final Object[] actuals = ArrayUtils.nullToEmpty(args); 932 return invokeMethod(object, methodName, actuals, ClassUtils.toClass(actuals)); 933 } 934 935 /** 936 * Invokes a named method whose parameter type matches the object type. 937 * 938 * <p> 939 * This method delegates the method search to {@link #getMatchingAccessibleMethod(Class, String, Class[])}. 940 * </p> 941 * <p> 942 * This method supports calls to methods taking primitive parameters 943 * via passing in wrapping classes. So, for example, a {@link Boolean} object 944 * would match a {@code boolean} primitive. 945 * </p> 946 * 947 * @param object invoke method on this object 948 * @param methodName get method with this name 949 * @param args use these arguments - treat null as empty array 950 * @param parameterTypes match these parameters - treat null as empty array 951 * @return The value returned by the invoked method 952 * @throws NoSuchMethodException Thrown if there is no such accessible method. 953 * @throws IllegalAccessException Thrown if this found {@code Method} is enforcing Java language access control and the underlying method is 954 * inaccessible. 955 * @throws IllegalArgumentException Thrown if: 956 * <ul> 957 * <li>the found {@code Method} is an instance method and the specified {@code object} argument is not an instance of 958 * the class or interface declaring the underlying method (or of a subclass or interface implementor);</li> 959 * <li>the number of actual and formal parameters differ;</li> 960 * <li>an unwrapping conversion for primitive arguments fails; or</li> 961 * <li>after possible unwrapping, a parameter value can't be converted to the corresponding formal parameter type by a 962 * method invocation conversion.</li> 963 * </ul> 964 * @throws InvocationTargetException Thrown if the underlying method throws an exception. 965 * @throws NullPointerException Thrown if the specified {@code object} is null. 966 * @throws ExceptionInInitializerError Thrown if the initialization provoked by this method fails. 967 * @see SecurityManager#checkPermission 968 */ 969 public static Object invokeMethod(final Object object, final String methodName, final Object[] args, final Class<?>[] parameterTypes) 970 throws NoSuchMethodException, IllegalAccessException, InvocationTargetException { 971 return invokeMethod(object, false, methodName, args, parameterTypes); 972 } 973 974 /** 975 * Invokes a named {@code static} method whose parameter type matches the object type. 976 * 977 * <p> 978 * This method delegates the method search to {@link #getMatchingAccessibleMethod(Class, String, Class[])}. 979 * </p> 980 * <p> 981 * This method supports calls to methods taking primitive parameters 982 * via passing in wrapping classes. So, for example, a {@link Boolean} class 983 * would match a {@code boolean} primitive. 984 * </p> 985 * <p> 986 * This is a convenient wrapper for 987 * {@link #invokeStaticMethod(Class, String, Object[], Class[])}. 988 * </p> 989 * 990 * @param cls invoke static method on this class 991 * @param methodName get method with this name 992 * @param args use these arguments - treat {@code null} as empty array 993 * @return The value returned by the invoked method 994 * @throws NoSuchMethodException Thrown if there is no such accessible method. 995 * @throws IllegalAccessException Thrown if this found {@code Method} is enforcing Java language access control and the underlying method is 996 * inaccessible. 997 * @throws IllegalArgumentException Thrown if: 998 * <ul> 999 * <li>the found {@code Method} is an instance method and the specified {@code object} argument is not an instance of 1000 * the class or interface declaring the underlying method (or of a subclass or interface implementor);</li> 1001 * <li>the number of actual and formal parameters differ;</li> 1002 * <li>an unwrapping conversion for primitive arguments fails; or</li> 1003 * <li>after possible unwrapping, a parameter value can't be converted to the corresponding formal parameter type by a 1004 * method invocation conversion.</li> 1005 * </ul> 1006 * @throws InvocationTargetException Thrown if the underlying method throws an exception. 1007 * @throws ExceptionInInitializerError Thrown if the initialization provoked by this method fails. 1008 * @see SecurityManager#checkPermission 1009 */ 1010 public static Object invokeStaticMethod(final Class<?> cls, final String methodName, final Object... args) 1011 throws NoSuchMethodException, IllegalAccessException, InvocationTargetException { 1012 final Object[] actuals = ArrayUtils.nullToEmpty(args); 1013 return invokeStaticMethod(cls, methodName, actuals, ClassUtils.toClass(actuals)); 1014 } 1015 1016 /** 1017 * Invokes a named {@code static} method whose parameter type matches the object type. 1018 * 1019 * <p> 1020 * This method delegates the method search to {@link #getMatchingAccessibleMethod(Class, String, Class[])}. 1021 * </p> 1022 * <p> 1023 * This method supports calls to methods taking primitive parameters 1024 * via passing in wrapping classes. So, for example, a {@link Boolean} class 1025 * would match a {@code boolean} primitive. 1026 * </p> 1027 * 1028 * @param cls invoke static method on this class 1029 * @param methodName get method with this name 1030 * @param args use these arguments - treat {@code null} as empty array 1031 * @param parameterTypes match these parameters - treat {@code null} as empty array 1032 * @return The value returned by the invoked method 1033 * @throws NoSuchMethodException Thrown if there is no such accessible method. 1034 * @throws IllegalAccessException Thrown if this found {@code Method} is enforcing Java language access control and the underlying method is 1035 * inaccessible. 1036 * @throws IllegalArgumentException Thrown if: 1037 * <ul> 1038 * <li>the found {@code Method} is an instance method and the specified {@code object} argument is not an instance of 1039 * the class or interface declaring the underlying method (or of a subclass or interface implementor);</li> 1040 * <li>the number of actual and formal parameters differ;</li> 1041 * <li>an unwrapping conversion for primitive arguments fails; or</li> 1042 * <li>after possible unwrapping, a parameter value can't be converted to the corresponding formal parameter type by a 1043 * method invocation conversion.</li> 1044 * </ul> 1045 * @throws InvocationTargetException Thrown if the underlying method throws an exception. 1046 * @throws ExceptionInInitializerError Thrown if the initialization provoked by this method fails. 1047 * @see SecurityManager#checkPermission 1048 */ 1049 public static Object invokeStaticMethod(final Class<?> cls, final String methodName, final Object[] args, final Class<?>[] parameterTypes) 1050 throws NoSuchMethodException, IllegalAccessException, InvocationTargetException { 1051 final Method method = getMatchingAccessibleMethod(cls, methodName, ArrayUtils.nullToEmpty(parameterTypes)); 1052 if (method == null) { 1053 throw new NoSuchMethodException("No such accessible method: " + methodName + "() on class: " + cls.getName()); 1054 } 1055 return method.invoke(null, toVarArgs(method, ArrayUtils.nullToEmpty(args))); 1056 } 1057 1058 private static Object[] toVarArgs(final Method method, final Object[] args) { 1059 return method.isVarArgs() ? getVarArgs(args, method.getParameterTypes()) : args; 1060 } 1061 1062 /** 1063 * {@link MethodUtils} instances should NOT be constructed in standard programming. Instead, the class should be used as 1064 * {@code MethodUtils.getAccessibleMethod(method)}. 1065 * 1066 * <p> 1067 * This constructor is {@code public} to permit tools that require a JavaBean instance to operate. 1068 * </p> 1069 * 1070 * @deprecated TODO Make private in 4.0. 1071 */ 1072 @Deprecated 1073 public MethodUtils() { 1074 // empty 1075 } 1076}