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