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