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