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