View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    *
9    *      http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  package org.apache.commons.lang3.reflect;
18  
19  import static org.hamcrest.MatcherAssert.assertThat;
20  import static org.hamcrest.Matchers.hasItemInArray;
21  import static org.hamcrest.Matchers.hasItems;
22  import static org.junit.jupiter.api.Assertions.assertArrayEquals;
23  import static org.junit.jupiter.api.Assertions.assertEquals;
24  import static org.junit.jupiter.api.Assertions.assertFalse;
25  import static org.junit.jupiter.api.Assertions.assertNotNull;
26  import static org.junit.jupiter.api.Assertions.assertNotSame;
27  import static org.junit.jupiter.api.Assertions.assertNull;
28  import static org.junit.jupiter.api.Assertions.assertSame;
29  import static org.junit.jupiter.api.Assertions.assertThrows;
30  import static org.junit.jupiter.api.Assertions.assertTrue;
31  
32  import java.awt.Color;
33  import java.lang.reflect.Method;
34  import java.lang.reflect.Type;
35  import java.util.Arrays;
36  import java.util.Date;
37  import java.util.HashMap;
38  import java.util.Iterator;
39  import java.util.List;
40  import java.util.Map;
41  
42  import org.apache.commons.lang3.AbstractLangTest;
43  import org.apache.commons.lang3.ArrayUtils;
44  import org.apache.commons.lang3.ClassUtils;
45  import org.apache.commons.lang3.ClassUtils.Interfaces;
46  import org.apache.commons.lang3.math.NumberUtils;
47  import org.apache.commons.lang3.mutable.Mutable;
48  import org.apache.commons.lang3.mutable.MutableObject;
49  import org.apache.commons.lang3.reflect.testbed.Annotated;
50  import org.apache.commons.lang3.reflect.testbed.GenericConsumer;
51  import org.apache.commons.lang3.reflect.testbed.GenericParent;
52  import org.apache.commons.lang3.reflect.testbed.PublicChild;
53  import org.apache.commons.lang3.reflect.testbed.StringParameterizedChild;
54  import org.apache.commons.lang3.tuple.ImmutablePair;
55  import org.junit.jupiter.api.BeforeEach;
56  import org.junit.jupiter.api.Test;
57  
58  /**
59   * Unit tests MethodUtils
60   */
61  public class MethodUtilsTest extends AbstractLangTest {
62  
63      protected abstract static class AbstractGetMatchingMethod {
64          public abstract void testMethod5(Exception exception);
65      }
66  
67      interface ChildInterface {
68      }
69  
70      public static class ChildObject extends ParentObject implements ChildInterface {
71      }
72  
73      private static final class GetMatchingMethodClass {
74          public void testMethod() {
75          }
76  
77          public void testMethod(final long aLong) {
78          }
79  
80          public void testMethod(final Long aLong) {
81          }
82  
83          public void testMethod2(final Color aColor) {
84          }
85  
86          public void testMethod2(final long aLong) {
87          }
88  
89          public void testMethod2(final Long aLong) {
90          }
91  
92          public void testMethod3(final long aLong, final Long anotherLong) {
93          }
94  
95          public void testMethod3(final Long aLong, final long anotherLong) {
96          }
97  
98          public void testMethod3(final Long aLong, final Long anotherLong) {
99          }
100 
101         public void testMethod4(final Color aColor1, final Color aColor2) {
102         }
103 
104         public void testMethod4(final Long aLong, final Long anotherLong) {
105         }
106     }
107 
108     private static final class GetMatchingMethodImpl extends AbstractGetMatchingMethod {
109         @Override
110         public void testMethod5(final Exception exception) {
111         }
112     }
113     public static class GrandParentObject {
114     }
115 
116     public static class InheritanceBean {
117         public void testOne(final GrandParentObject obj) {
118         }
119 
120         public void testOne(final Object obj) {
121         }
122 
123         public void testOne(final ParentObject obj) {
124         }
125 
126         public void testTwo(final ChildInterface obj) {
127         }
128 
129         public void testTwo(final GrandParentObject obj) {
130         }
131 
132         public void testTwo(final Object obj) {
133         }
134     }
135 
136     private static final class MethodDescriptor {
137         final Class<?> declaringClass;
138         final String name;
139         final Type[] parameterTypes;
140 
141         MethodDescriptor(final Class<?> declaringClass, final String name, final Type... parameterTypes) {
142             this.declaringClass = declaringClass;
143             this.name = name;
144             this.parameterTypes = parameterTypes;
145         }
146     }
147 
148     public static class ParentObject extends GrandParentObject {
149     }
150 
151     private interface PrivateInterface {
152     }
153 
154     public static class TestBean {
155 
156         public static String bar() {
157             return "bar()";
158         }
159 
160         public static String bar(final double d) {
161             return "bar(double)";
162         }
163 
164         public static String bar(final int i) {
165             return "bar(int)";
166         }
167 
168         public static String bar(final Integer i) {
169             return "bar(Integer)";
170         }
171 
172         public static String bar(final Integer i, final String... s) {
173             return "bar(int, String...)";
174         }
175 
176         public static String bar(final long... s) {
177             return "bar(long...)";
178         }
179 
180         public static String bar(final Object o) {
181             return "bar(Object)";
182         }
183 
184         public static String bar(final String s) {
185             return "bar(String)";
186         }
187 
188         public static String bar(final String... s) {
189             return "bar(String...)";
190         }
191 
192         // This method is overloaded for the wrapper class for every numeric primitive type, plus the common
193         // supertype Number
194         public static String numOverload(final Byte... args) {
195             return "Byte...";
196         }
197 
198         public static String numOverload(final Double... args) {
199             return "Double...";
200         }
201 
202         public static String numOverload(final Float... args) {
203             return "Float...";
204         }
205 
206         public static String numOverload(final Integer... args) {
207             return "Integer...";
208         }
209 
210         public static String numOverload(final Long... args) {
211             return "Long...";
212         }
213 
214         public static String numOverload(final Number... args) {
215             return "Number...";
216         }
217 
218         public static String numOverload(final Short... args) {
219             return "Short...";
220         }
221 
222         public static void oneParameterStatic(final String s) {
223             // empty
224         }
225 
226         public static String varOverload(final Boolean... args) {
227             return "Boolean...";
228         }
229 
230         // This method is overloaded for the wrapper class for every primitive type, plus the common supertypes
231         // Number and Object. This is an acid test since it easily leads to ambiguous methods.
232         public static String varOverload(final Byte... args) {
233             return "Byte...";
234         }
235 
236         public static String varOverload(final Character... args) {
237             return "Character...";
238         }
239 
240         public static String varOverload(final Double... args) {
241             return "Double...";
242         }
243 
244         public static String varOverload(final Float... args) {
245             return "Float...";
246         }
247 
248         public static String varOverload(final Integer... args) {
249             return "Integer...";
250         }
251 
252         public static String varOverload(final Long... args) {
253             return "Long...";
254         }
255 
256         public static String varOverload(final Number... args) {
257             return "Number...";
258         }
259 
260         public static String varOverload(final Object... args) {
261             return "Object...";
262         }
263 
264         public static String varOverload(final Short... args) {
265             return "Short...";
266         }
267 
268         public static String varOverload(final String... args) {
269             return "String...";
270         }
271 
272         public static ImmutablePair<String, Object[]> varOverloadEchoStatic(final Number... args) {
273             return new ImmutablePair<>("Number...", args);
274         }
275 
276         public static ImmutablePair<String, Object[]> varOverloadEchoStatic(final String... args) {
277             return new ImmutablePair<>("String...", args);
278         }
279 
280         static void verify(final ImmutablePair<String, Object[]> a, final ImmutablePair<String, Object[]> b) {
281             assertEquals(a.getLeft(), b.getLeft());
282             assertArrayEquals(a.getRight(), b.getRight());
283         }
284 
285         static void verify(final ImmutablePair<String, Object[]> a, final Object obj) {
286             @SuppressWarnings("unchecked")
287             final ImmutablePair<String, Object[]> pair = (ImmutablePair<String, Object[]>) obj;
288             verify(a, pair);
289         }
290 
291         public String foo() {
292             return "foo()";
293         }
294 
295         public String foo(final double d) {
296             return "foo(double)";
297         }
298 
299         public String foo(final int i) {
300             return "foo(int)";
301         }
302 
303         public String foo(final Integer i) {
304             return "foo(Integer)";
305         }
306 
307         public String foo(final Integer i, final String... s) {
308             return "foo(int, String...)";
309         }
310 
311         public String foo(final long l) {
312             return "foo(long)";
313         }
314 
315         public String foo(final long... l) {
316             return "foo(long...)";
317         }
318 
319         public String foo(final Object o) {
320             return "foo(Object)";
321         }
322 
323         public String foo(final Object... s) {
324             return "foo(Object...)";
325         }
326 
327         public String foo(final String s) {
328             return "foo(String)";
329         }
330 
331         public String foo(final String... s) {
332             return "foo(String...)";
333         }
334 
335         public void oneParameter(final String s) {
336             // empty
337         }
338 
339         @SuppressWarnings("unused")
340         private String privateStringStuff() {
341             return "privateStringStuff()";
342         }
343 
344         @SuppressWarnings("unused")
345         private String privateStringStuff(final double d) {
346             return "privateStringStuff(double)";
347         }
348 
349         @SuppressWarnings("unused")
350         private String privateStringStuff(final int i) {
351             return "privateStringStuff(int)";
352         }
353 
354         @SuppressWarnings("unused")
355         private String privateStringStuff(final Integer i) {
356             return "privateStringStuff(Integer)";
357         }
358 
359         @SuppressWarnings("unused")
360         private String privateStringStuff(final Object s) {
361             return "privateStringStuff(Object)";
362         }
363 
364         @SuppressWarnings("unused")
365         private String privateStringStuff(final String s) {
366             return "privateStringStuff(String)";
367         }
368 
369         @SuppressWarnings("unused")
370         private void privateStuff() {
371         }
372 
373         public int[] unboxing(final int... values) {
374             return values;
375         }
376 
377         public ImmutablePair<String, Object[]> varOverloadEcho(final Number... args) {
378             return new ImmutablePair<>("Number...", args);
379         }
380 
381         // These varOverloadEcho and varOverloadEchoStatic methods are designed to verify that
382         // not only is the correct overloaded variant invoked, but that the varags arguments
383         // are also delivered correctly to the method.
384         public ImmutablePair<String, Object[]> varOverloadEcho(final String... args) {
385             return new ImmutablePair<>("String...", args);
386         }
387 
388     }
389 
390     static class TestBeanWithInterfaces implements PrivateInterface {
391         public String foo() {
392             return "foo()";
393         }
394     }
395 
396     private static final class TestMutable implements Mutable<Object> {
397         @Override
398         public Object getValue() {
399             return null;
400         }
401 
402         @Override
403         public void setValue(final Object value) {
404         }
405     }
406 
407     private TestBean testBean;
408 
409     private final Map<Class<?>, Class<?>[]> classCache = new HashMap<>();
410 
411     private void expectMatchingAccessibleMethodParameterTypes(final Class<?> cls,
412                                                               final String methodName, final Class<?>[] requestTypes, final Class<?>[] actualTypes) {
413         final Method m = MethodUtils.getMatchingAccessibleMethod(cls, methodName,
414                 requestTypes);
415         assertNotNull(m, "could not find any matches for " + methodName
416                 + " (" + (requestTypes == null ? null : toString(requestTypes)) + ")");
417         assertArrayEquals(actualTypes, m.getParameterTypes(), toString(m.getParameterTypes()) + " not equals " + toString(actualTypes));
418     }
419 
420     @BeforeEach
421     public void setUp() {
422         testBean = new TestBean();
423         classCache.clear();
424     }
425 
426     private Class<?>[] singletonArray(final Class<?> c) {
427         Class<?>[] result = classCache.get(c);
428         if (result == null) {
429             result = new Class[]{c};
430             classCache.put(c, result);
431         }
432         return result;
433     }
434 
435     @Test
436     public void testConstructor() throws Exception {
437         assertNotNull(MethodUtils.class.getConstructor().newInstance());
438     }
439 
440     @Test
441     public void testDistance() throws Exception {
442         final Method distanceMethod = MethodUtils.getMatchingMethod(MethodUtils.class, "distance", Class[].class, Class[].class);
443         distanceMethod.setAccessible(true);
444 
445         assertEquals(-1, distanceMethod.invoke(null, new Class[]{String.class}, new Class[]{Date.class}));
446         assertEquals(0, distanceMethod.invoke(null, new Class[]{Date.class}, new Class[]{Date.class}));
447         assertEquals(1, distanceMethod.invoke(null, new Class[]{Integer.class}, new Class[]{ClassUtils.wrapperToPrimitive(Integer.class)}));
448         assertEquals(2, distanceMethod.invoke(null, new Class[]{Integer.class}, new Class[]{Object.class}));
449 
450         distanceMethod.setAccessible(false);
451     }
452 
453     @Test
454     public void testGetAccessibleInterfaceMethod() throws Exception {
455         final Class<?>[][] p = {ArrayUtils.EMPTY_CLASS_ARRAY, null};
456         for (final Class<?>[] element : p) {
457             final Method method = TestMutable.class.getMethod("getValue", element);
458             final Method accessibleMethod = MethodUtils.getAccessibleMethod(method);
459             assertNotSame(accessibleMethod, method);
460             assertSame(Mutable.class, accessibleMethod.getDeclaringClass());
461         }
462     }
463 
464     @Test
465     public void testGetAccessibleInterfaceMethodFromDescription() {
466         final Class<?>[][] p = {ArrayUtils.EMPTY_CLASS_ARRAY, null};
467         for (final Class<?>[] element : p) {
468             final Method accessibleMethod = MethodUtils.getAccessibleMethod(
469                     TestMutable.class, "getValue", element);
470             assertSame(Mutable.class, accessibleMethod.getDeclaringClass());
471         }
472     }
473 
474     @Test
475     public void testGetAccessibleMethodInaccessible() throws Exception {
476         final Method expected = TestBean.class.getDeclaredMethod("privateStuff");
477         final Method actual = MethodUtils.getAccessibleMethod(expected);
478         assertNull(actual);
479     }
480 
481     @Test
482     public void testGetAccessibleMethodPrivateInterface() throws Exception {
483         final Method expected = TestBeanWithInterfaces.class.getMethod("foo");
484         assertNotNull(expected);
485         final Method actual = MethodUtils.getAccessibleMethod(TestBeanWithInterfaces.class, "foo");
486         assertNull(actual);
487     }
488 
489     @Test
490     public void testGetAccessiblePublicMethod() throws Exception {
491         assertSame(MutableObject.class, MethodUtils.getAccessibleMethod(
492                 MutableObject.class.getMethod("getValue",
493                         ArrayUtils.EMPTY_CLASS_ARRAY)).getDeclaringClass());
494     }
495 
496     @Test
497     public void testGetAccessiblePublicMethodFromDescription() {
498         assertSame(MutableObject.class, MethodUtils.getAccessibleMethod(
499                 MutableObject.class, "getValue", ArrayUtils.EMPTY_CLASS_ARRAY)
500                 .getDeclaringClass());
501     }
502 
503     @Test
504     public void testGetAnnotationIllegalArgumentException1() {
505         assertThrows(NullPointerException.class,
506                 () -> MethodUtils.getAnnotation(FieldUtilsTest.class.getDeclaredMethods()[0], null, true, true));
507     }
508 
509     @Test
510     public void testGetAnnotationIllegalArgumentException2() {
511         assertThrows(NullPointerException.class, () -> MethodUtils.getAnnotation(null, Annotated.class, true, true));
512     }
513 
514     @Test
515     public void testGetAnnotationIllegalArgumentException3() {
516         assertThrows(NullPointerException.class, () -> MethodUtils.getAnnotation(null, null, true, true));
517     }
518 
519     @Test
520     public void testGetAnnotationNotSearchSupersAndNotIgnoreAccess() throws NoSuchMethodException {
521         assertNull(MethodUtils.getAnnotation(PublicChild.class.getMethod("parentNotAnnotatedMethod"),
522                 Annotated.class, false, false));
523         assertNull(MethodUtils.getAnnotation(PublicChild.class.getMethod("doIt"), Annotated.class,
524                 false, false));
525         assertNull(MethodUtils.getAnnotation(PublicChild.class.getMethod("parentProtectedAnnotatedMethod"),
526                 Annotated.class, false, false));
527         assertNull(MethodUtils.getAnnotation(PublicChild.class.getDeclaredMethod("privateAnnotatedMethod"),
528                 Annotated.class, false, false));
529         assertNotNull(MethodUtils.getAnnotation(PublicChild.class.getMethod("publicAnnotatedMethod"),
530                 Annotated.class, false, false));
531     }
532 
533     @Test
534     public void testGetAnnotationNotSearchSupersButIgnoreAccess() throws NoSuchMethodException {
535         assertNull(MethodUtils.getAnnotation(PublicChild.class.getMethod("parentNotAnnotatedMethod"),
536                 Annotated.class, false, true));
537         assertNull(MethodUtils.getAnnotation(PublicChild.class.getMethod("doIt"), Annotated.class,
538                 false, true));
539         assertNull(MethodUtils.getAnnotation(PublicChild.class.getMethod("parentProtectedAnnotatedMethod"),
540                 Annotated.class, false, true));
541         assertNotNull(MethodUtils.getAnnotation(PublicChild.class.getDeclaredMethod("privateAnnotatedMethod"),
542                 Annotated.class, false, true));
543         assertNotNull(MethodUtils.getAnnotation(PublicChild.class.getMethod("publicAnnotatedMethod"),
544                 Annotated.class, false, true));
545     }
546 
547     @Test
548     public void testGetAnnotationSearchSupersAndIgnoreAccess() throws NoSuchMethodException {
549         assertNull(MethodUtils.getAnnotation(PublicChild.class.getMethod("parentNotAnnotatedMethod"),
550                 Annotated.class, true, true));
551         assertNotNull(MethodUtils.getAnnotation(PublicChild.class.getMethod("doIt"), Annotated.class,
552                 true, true));
553         assertNotNull(MethodUtils.getAnnotation(PublicChild.class.getMethod("parentProtectedAnnotatedMethod"),
554                 Annotated.class, true, true));
555         assertNotNull(MethodUtils.getAnnotation(PublicChild.class.getDeclaredMethod("privateAnnotatedMethod"),
556                 Annotated.class, true, true));
557         assertNotNull(MethodUtils.getAnnotation(PublicChild.class.getMethod("publicAnnotatedMethod"),
558                 Annotated.class, true, true));
559 
560         assertNull(MethodUtils.getAnnotation(StringParameterizedChild.class.getMethod("parentNotAnnotatedMethod", String.class),
561                 Annotated.class, true, true));
562         assertNotNull(MethodUtils.getAnnotation(StringParameterizedChild.class.getMethod("parentProtectedAnnotatedMethod", String.class),
563                 Annotated.class, true, true));
564         assertNotNull(MethodUtils.getAnnotation(StringParameterizedChild.class.getDeclaredMethod("privateAnnotatedMethod", String.class),
565                 Annotated.class, true, true));
566         assertNotNull(MethodUtils.getAnnotation(StringParameterizedChild.class.getMethod("publicAnnotatedMethod", String.class),
567                 Annotated.class, true, true));
568     }
569 
570     @Test
571     public void testGetAnnotationSearchSupersButNotIgnoreAccess() throws NoSuchMethodException {
572         assertNull(MethodUtils.getAnnotation(PublicChild.class.getMethod("parentNotAnnotatedMethod"),
573                 Annotated.class, true, false));
574         assertNull(MethodUtils.getAnnotation(PublicChild.class.getMethod("doIt"), Annotated.class,
575                 true, false));
576         assertNull(MethodUtils.getAnnotation(PublicChild.class.getMethod("parentProtectedAnnotatedMethod"),
577                 Annotated.class, true, false));
578         assertNull(MethodUtils.getAnnotation(PublicChild.class.getDeclaredMethod("privateAnnotatedMethod"),
579                 Annotated.class, true, false));
580         assertNotNull(MethodUtils.getAnnotation(PublicChild.class.getMethod("publicAnnotatedMethod"),
581                 Annotated.class, true, false));
582 
583         assertNull(MethodUtils.getAnnotation(StringParameterizedChild.class.getMethod("parentNotAnnotatedMethod", String.class),
584                 Annotated.class, true, false));
585         assertNull(MethodUtils.getAnnotation(StringParameterizedChild.class.getMethod("parentProtectedAnnotatedMethod", String.class),
586                 Annotated.class, true, false));
587         assertNull(MethodUtils.getAnnotation(StringParameterizedChild.class.getDeclaredMethod("privateAnnotatedMethod", String.class),
588                 Annotated.class, true, false));
589         assertNotNull(MethodUtils.getAnnotation(StringParameterizedChild.class.getMethod("publicAnnotatedMethod", String.class),
590                 Annotated.class, true, false));
591     }
592 
593     @Test
594     public void testGetMatchingAccessibleMethod() {
595         expectMatchingAccessibleMethodParameterTypes(TestBean.class, "foo",
596                 ArrayUtils.EMPTY_CLASS_ARRAY, ArrayUtils.EMPTY_CLASS_ARRAY);
597         expectMatchingAccessibleMethodParameterTypes(TestBean.class, "foo",
598                 null, ArrayUtils.EMPTY_CLASS_ARRAY);
599         expectMatchingAccessibleMethodParameterTypes(TestBean.class, "foo",
600                 singletonArray(String.class), singletonArray(String.class));
601         expectMatchingAccessibleMethodParameterTypes(TestBean.class, "foo",
602                 singletonArray(Object.class), singletonArray(Object.class));
603         expectMatchingAccessibleMethodParameterTypes(TestBean.class, "foo",
604                 singletonArray(Boolean.class), singletonArray(Object.class));
605         expectMatchingAccessibleMethodParameterTypes(TestBean.class, "foo",
606                 singletonArray(Byte.class), singletonArray(Integer.TYPE));
607         expectMatchingAccessibleMethodParameterTypes(TestBean.class, "foo",
608                 singletonArray(Byte.TYPE), singletonArray(Integer.TYPE));
609         expectMatchingAccessibleMethodParameterTypes(TestBean.class, "foo",
610                 singletonArray(Short.class), singletonArray(Integer.TYPE));
611         expectMatchingAccessibleMethodParameterTypes(TestBean.class, "foo",
612                 singletonArray(Short.TYPE), singletonArray(Integer.TYPE));
613         expectMatchingAccessibleMethodParameterTypes(TestBean.class, "foo",
614                 singletonArray(Character.class), singletonArray(Integer.TYPE));
615         expectMatchingAccessibleMethodParameterTypes(TestBean.class, "foo",
616                 singletonArray(Character.TYPE), singletonArray(Integer.TYPE));
617         expectMatchingAccessibleMethodParameterTypes(TestBean.class, "foo",
618                 singletonArray(Integer.class), singletonArray(Integer.class));
619         expectMatchingAccessibleMethodParameterTypes(TestBean.class, "foo",
620                 singletonArray(Integer.TYPE), singletonArray(Integer.TYPE));
621         expectMatchingAccessibleMethodParameterTypes(TestBean.class, "foo",
622                 singletonArray(Long.class), singletonArray(Long.TYPE));
623         expectMatchingAccessibleMethodParameterTypes(TestBean.class, "foo",
624                 singletonArray(Long.TYPE), singletonArray(Long.TYPE));
625         expectMatchingAccessibleMethodParameterTypes(TestBean.class, "foo",
626                 singletonArray(Float.class), singletonArray(Double.TYPE));
627         expectMatchingAccessibleMethodParameterTypes(TestBean.class, "foo",
628                 singletonArray(Float.TYPE), singletonArray(Double.TYPE));
629         expectMatchingAccessibleMethodParameterTypes(TestBean.class, "foo",
630                 singletonArray(Double.class), singletonArray(Double.TYPE));
631         expectMatchingAccessibleMethodParameterTypes(TestBean.class, "foo",
632                 singletonArray(Double.TYPE), singletonArray(Double.TYPE));
633         expectMatchingAccessibleMethodParameterTypes(TestBean.class, "foo",
634                 singletonArray(Double.TYPE), singletonArray(Double.TYPE));
635         expectMatchingAccessibleMethodParameterTypes(TestBean.class, "foo",
636                 new Class[]{String.class, String.class}, new Class[]{String[].class});
637         expectMatchingAccessibleMethodParameterTypes(TestBean.class, "foo",
638                 new Class[]{Integer.TYPE, String.class, String.class}, new Class[]{Integer.class, String[].class});
639         expectMatchingAccessibleMethodParameterTypes(InheritanceBean.class, "testOne",
640                 singletonArray(ParentObject.class), singletonArray(ParentObject.class));
641         expectMatchingAccessibleMethodParameterTypes(InheritanceBean.class, "testOne",
642                 singletonArray(ChildObject.class), singletonArray(ParentObject.class));
643         expectMatchingAccessibleMethodParameterTypes(InheritanceBean.class, "testTwo",
644                 singletonArray(ParentObject.class), singletonArray(GrandParentObject.class));
645         expectMatchingAccessibleMethodParameterTypes(InheritanceBean.class, "testTwo",
646                 singletonArray(ChildObject.class), singletonArray(ChildInterface.class));
647     }
648 
649     @Test
650     public void testGetMatchingMethod() throws NoSuchMethodException {
651         assertEquals(MethodUtils.getMatchingMethod(GetMatchingMethodClass.class, "testMethod"),
652                 GetMatchingMethodClass.class.getMethod("testMethod"));
653 
654         assertEquals(MethodUtils.getMatchingMethod(GetMatchingMethodClass.class, "testMethod", Long.TYPE),
655                 GetMatchingMethodClass.class.getMethod("testMethod", Long.TYPE));
656 
657         assertEquals(MethodUtils.getMatchingMethod(GetMatchingMethodClass.class, "testMethod", Long.class),
658                 GetMatchingMethodClass.class.getMethod("testMethod", Long.class));
659 
660         assertEquals(MethodUtils.getMatchingMethod(GetMatchingMethodClass.class, "testMethod", (Class<?>) null),
661                 GetMatchingMethodClass.class.getMethod("testMethod", Long.class));
662 
663         assertThrows(IllegalStateException.class,
664                 () -> MethodUtils.getMatchingMethod(GetMatchingMethodClass.class, "testMethod2", (Class<?>) null));
665 
666         assertEquals(MethodUtils.getMatchingMethod(GetMatchingMethodClass.class, "testMethod3", Long.TYPE, Long.class),
667                 GetMatchingMethodClass.class.getMethod("testMethod3", Long.TYPE, Long.class));
668 
669         assertEquals(MethodUtils.getMatchingMethod(GetMatchingMethodClass.class, "testMethod3", Long.class, Long.TYPE),
670                 GetMatchingMethodClass.class.getMethod("testMethod3", Long.class, Long.TYPE));
671 
672         assertEquals(MethodUtils.getMatchingMethod(GetMatchingMethodClass.class, "testMethod3", null, Long.TYPE),
673                 GetMatchingMethodClass.class.getMethod("testMethod3", Long.class, Long.TYPE));
674 
675         assertEquals(MethodUtils.getMatchingMethod(GetMatchingMethodClass.class, "testMethod3", Long.TYPE, null),
676                 GetMatchingMethodClass.class.getMethod("testMethod3", Long.TYPE, Long.class));
677 
678         assertThrows(IllegalStateException.class,
679                 () -> MethodUtils.getMatchingMethod(GetMatchingMethodClass.class, "testMethod4", null, null));
680 
681         assertEquals(MethodUtils.getMatchingMethod(GetMatchingMethodImpl.class, "testMethod5", RuntimeException.class),
682                 GetMatchingMethodImpl.class.getMethod("testMethod5", Exception.class));
683 
684         assertThrows(NullPointerException.class,
685                 () -> MethodUtils.getMatchingMethod(null, "testMethod5", RuntimeException.class));
686     }
687 
688     @Test
689     @Annotated
690     public void testGetMethodsListWithAnnotation() throws NoSuchMethodException {
691         assertEquals(0, MethodUtils.getMethodsListWithAnnotation(Object.class, Annotated.class).size());
692 
693         final List<Method> methodWithAnnotation = MethodUtils.getMethodsListWithAnnotation(MethodUtilsTest.class, Annotated.class);
694         assertEquals(2, methodWithAnnotation.size());
695         assertThat(methodWithAnnotation, hasItems(
696                 MethodUtilsTest.class.getMethod("testGetMethodsWithAnnotation"),
697                 MethodUtilsTest.class.getMethod("testGetMethodsListWithAnnotation")
698         ));
699     }
700 
701     @Test
702     public void testGetMethodsListWithAnnotationNullPointerException1() {
703         assertThrows(NullPointerException.class, () -> MethodUtils.getMethodsListWithAnnotation(FieldUtilsTest.class, null));
704     }
705 
706     @Test
707     public void testGetMethodsListWithAnnotationNullPointerException2() {
708         assertThrows(NullPointerException.class, () -> MethodUtils.getMethodsListWithAnnotation(null, Annotated.class));
709     }
710 
711     @Test
712     public void testGetMethodsListWithAnnotationNullPointerException3() {
713         assertThrows(NullPointerException.class, () -> MethodUtils.getMethodsListWithAnnotation(null, null));
714     }
715 
716     @Test
717     @Annotated
718     public void testGetMethodsWithAnnotation() throws NoSuchMethodException {
719         assertArrayEquals(new Method[0], MethodUtils.getMethodsWithAnnotation(Object.class, Annotated.class));
720 
721         final Method[] methodsWithAnnotation = MethodUtils.getMethodsWithAnnotation(MethodUtilsTest.class, Annotated.class);
722         assertEquals(2, methodsWithAnnotation.length);
723         assertThat(methodsWithAnnotation, hasItemInArray(MethodUtilsTest.class.getMethod("testGetMethodsWithAnnotation")));
724         assertThat(methodsWithAnnotation, hasItemInArray(MethodUtilsTest.class.getMethod("testGetMethodsListWithAnnotation")));
725     }
726 
727     @Test
728     public void testGetMethodsWithAnnotationIllegalArgumentException1() {
729         assertThrows(NullPointerException.class, () -> MethodUtils.getMethodsWithAnnotation(FieldUtilsTest.class, null));
730     }
731 
732     @Test
733     public void testGetMethodsWithAnnotationIllegalArgumentException2() {
734         assertThrows(NullPointerException.class, () -> MethodUtils.getMethodsWithAnnotation(null, Annotated.class));
735     }
736 
737     @Test
738     public void testGetMethodsWithAnnotationIllegalArgumentException3() {
739         assertThrows(NullPointerException.class, () -> MethodUtils.getMethodsWithAnnotation(null, null));
740     }
741 
742     @Test
743     public void testGetMethodsWithAnnotationNotSearchSupersAndNotIgnoreAccess() {
744         assertArrayEquals(new Method[0], MethodUtils.getMethodsWithAnnotation(Object.class, Annotated.class,
745                 false, false));
746 
747         final Method[] methodsWithAnnotation = MethodUtils.getMethodsWithAnnotation(PublicChild.class, Annotated.class,
748                 false, false);
749         assertEquals(1, methodsWithAnnotation.length);
750         assertEquals("PublicChild.publicAnnotatedMethod",
751                 methodsWithAnnotation[0].getDeclaringClass().getSimpleName() + '.' +
752                         methodsWithAnnotation[0].getName());
753     }
754 
755     @Test
756     public void testGetMethodsWithAnnotationNotSearchSupersButIgnoreAccess() {
757         assertArrayEquals(new Method[0], MethodUtils.getMethodsWithAnnotation(Object.class, Annotated.class,
758                 false, true));
759 
760         final Method[] methodsWithAnnotation = MethodUtils.getMethodsWithAnnotation(PublicChild.class, Annotated.class,
761                 false, true);
762         assertEquals(2, methodsWithAnnotation.length);
763         assertEquals("PublicChild", methodsWithAnnotation[0].getDeclaringClass().getSimpleName());
764         assertEquals("PublicChild", methodsWithAnnotation[1].getDeclaringClass().getSimpleName());
765         assertTrue(methodsWithAnnotation[0].getName().endsWith("AnnotatedMethod"));
766         assertTrue(methodsWithAnnotation[1].getName().endsWith("AnnotatedMethod"));
767     }
768 
769     @Test
770     public void testGetMethodsWithAnnotationSearchSupersAndIgnoreAccess() {
771         assertArrayEquals(new Method[0], MethodUtils.getMethodsWithAnnotation(Object.class, Annotated.class,
772                 true, true));
773 
774         final Method[] methodsWithAnnotation = MethodUtils.getMethodsWithAnnotation(PublicChild.class, Annotated.class,
775                 true, true);
776         assertEquals(4, methodsWithAnnotation.length);
777         assertEquals("PublicChild", methodsWithAnnotation[0].getDeclaringClass().getSimpleName());
778         assertEquals("PublicChild", methodsWithAnnotation[1].getDeclaringClass().getSimpleName());
779         assertTrue(methodsWithAnnotation[0].getName().endsWith("AnnotatedMethod"));
780         assertTrue(methodsWithAnnotation[1].getName().endsWith("AnnotatedMethod"));
781         assertEquals("Foo.doIt",
782                 methodsWithAnnotation[2].getDeclaringClass().getSimpleName() + '.' +
783                         methodsWithAnnotation[2].getName());
784         assertEquals("Parent.parentProtectedAnnotatedMethod",
785                 methodsWithAnnotation[3].getDeclaringClass().getSimpleName() + '.' +
786                         methodsWithAnnotation[3].getName());
787     }
788 
789     @Test
790     public void testGetMethodsWithAnnotationSearchSupersButNotIgnoreAccess() {
791         assertArrayEquals(new Method[0], MethodUtils.getMethodsWithAnnotation(Object.class, Annotated.class,
792                 true, false));
793 
794         final Method[] methodsWithAnnotation = MethodUtils.getMethodsWithAnnotation(PublicChild.class, Annotated.class,
795                 true, false);
796         assertEquals(2, methodsWithAnnotation.length);
797         assertEquals("PublicChild.publicAnnotatedMethod",
798                 methodsWithAnnotation[0].getDeclaringClass().getSimpleName() + '.' +
799                         methodsWithAnnotation[0].getName());
800         assertEquals("Foo.doIt",
801                 methodsWithAnnotation[1].getDeclaringClass().getSimpleName() + '.' +
802                         methodsWithAnnotation[1].getName());
803     }
804 
805     @Test
806     public void testGetOverrideHierarchyExcludingInterfaces() {
807         final Method method = MethodUtils.getAccessibleMethod(StringParameterizedChild.class, "consume", String.class);
808         final Iterator<MethodDescriptor> expected =
809                 Arrays.asList(new MethodDescriptor(StringParameterizedChild.class, "consume", String.class),
810                         new MethodDescriptor(GenericParent.class, "consume", GenericParent.class.getTypeParameters()[0]))
811                         .iterator();
812         for (final Method m : MethodUtils.getOverrideHierarchy(method, Interfaces.EXCLUDE)) {
813             assertTrue(expected.hasNext());
814             final MethodDescriptor md = expected.next();
815             assertEquals(md.declaringClass, m.getDeclaringClass());
816             assertEquals(md.name, m.getName());
817             assertEquals(md.parameterTypes.length, m.getParameterTypes().length);
818             for (int i = 0; i < md.parameterTypes.length; i++) {
819                 assertTrue(TypeUtils.equals(md.parameterTypes[i], m.getGenericParameterTypes()[i]));
820             }
821         }
822         assertFalse(expected.hasNext());
823     }
824 
825     @Test
826     public void testGetOverrideHierarchyIncludingInterfaces() {
827         final Method method = MethodUtils.getAccessibleMethod(StringParameterizedChild.class, "consume", String.class);
828         final Iterator<MethodDescriptor> expected =
829                 Arrays.asList(new MethodDescriptor(StringParameterizedChild.class, "consume", String.class),
830                         new MethodDescriptor(GenericParent.class, "consume", GenericParent.class.getTypeParameters()[0]),
831                         new MethodDescriptor(GenericConsumer.class, "consume", GenericConsumer.class.getTypeParameters()[0]))
832                         .iterator();
833         for (final Method m : MethodUtils.getOverrideHierarchy(method, Interfaces.INCLUDE)) {
834             assertTrue(expected.hasNext());
835             final MethodDescriptor md = expected.next();
836             assertEquals(md.declaringClass, m.getDeclaringClass());
837             assertEquals(md.name, m.getName());
838             assertEquals(md.parameterTypes.length, m.getParameterTypes().length);
839             for (int i = 0; i < md.parameterTypes.length; i++) {
840                 assertTrue(TypeUtils.equals(md.parameterTypes[i], m.getGenericParameterTypes()[i]));
841             }
842         }
843         assertFalse(expected.hasNext());
844     }
845 
846     @Test
847     public void testInvokeExactMethod() throws Exception {
848         assertEquals("foo()", MethodUtils.invokeExactMethod(testBean, "foo",
849                 (Object[]) ArrayUtils.EMPTY_CLASS_ARRAY));
850         assertEquals("foo()", MethodUtils.invokeExactMethod(testBean, "foo"));
851         assertEquals("foo()", MethodUtils.invokeExactMethod(testBean, "foo",
852                 (Object[]) null));
853         assertEquals("foo()", MethodUtils.invokeExactMethod(testBean, "foo",
854                 null, null));
855         assertEquals("foo(String)", MethodUtils.invokeExactMethod(testBean,
856                 "foo", ""));
857         assertEquals("foo(Object)", MethodUtils.invokeExactMethod(testBean,
858                 "foo", new Object()));
859         assertEquals("foo(Integer)", MethodUtils.invokeExactMethod(testBean,
860                 "foo", NumberUtils.INTEGER_ONE));
861         assertEquals("foo(double)", MethodUtils.invokeExactMethod(testBean,
862                 "foo", new Object[]{NumberUtils.DOUBLE_ONE},
863                 new Class[]{Double.TYPE}));
864 
865         assertThrows(
866                 NoSuchMethodException.class,
867                 () -> MethodUtils.invokeExactMethod(testBean, "foo", NumberUtils.BYTE_ONE));
868 
869         assertThrows(
870                 NoSuchMethodException.class,
871                 () -> MethodUtils.invokeExactMethod(testBean, "foo", NumberUtils.LONG_ONE));
872         assertThrows(NoSuchMethodException.class, () -> MethodUtils.invokeExactMethod(testBean, "foo", Boolean.TRUE));
873 
874         assertThrows(
875                 NullPointerException.class,
876                 () -> MethodUtils.invokeExactMethod(null, "foo", NumberUtils.BYTE_ONE));
877         assertThrows(
878                 NullPointerException.class,
879                 () -> MethodUtils.invokeExactMethod(testBean, null, NumberUtils.BYTE_ONE));
880 
881         assertThrows(
882                 NullPointerException.class,
883                 () -> MethodUtils.invokeExactMethod(null, "foo", new Object[]{NumberUtils.DOUBLE_ONE},
884                         new Class[]{Double.TYPE}));
885         assertThrows(
886                 NullPointerException.class,
887                 () -> MethodUtils.invokeExactMethod(testBean, null, new Object[]{NumberUtils.DOUBLE_ONE},
888                         new Class[]{Double.TYPE}));
889     }
890 
891     @Test
892     public void testInvokeExactStaticMethod() throws Exception {
893         assertEquals("bar()", MethodUtils.invokeExactStaticMethod(TestBean.class,
894                 "bar", (Object[]) ArrayUtils.EMPTY_CLASS_ARRAY));
895         assertEquals("bar()", MethodUtils.invokeExactStaticMethod(TestBean.class,
896                 "bar", (Object[]) null));
897         assertEquals("bar()", MethodUtils.invokeExactStaticMethod(TestBean.class,
898                 "bar", null, null));
899         assertEquals("bar(String)", MethodUtils.invokeExactStaticMethod(
900                 TestBean.class, "bar", ""));
901         assertEquals("bar(Object)", MethodUtils.invokeExactStaticMethod(
902                 TestBean.class, "bar", new Object()));
903         assertEquals("bar(Integer)", MethodUtils.invokeExactStaticMethod(
904                 TestBean.class, "bar", NumberUtils.INTEGER_ONE));
905         assertEquals("bar(double)", MethodUtils.invokeExactStaticMethod(
906                 TestBean.class, "bar", new Object[]{NumberUtils.DOUBLE_ONE},
907                 new Class[]{Double.TYPE}));
908 
909         assertThrows(
910                 NoSuchMethodException.class,
911                 () -> MethodUtils.invokeExactStaticMethod(TestBean.class, "bar", NumberUtils.BYTE_ONE));
912         assertThrows(
913                 NoSuchMethodException.class,
914                 () -> MethodUtils.invokeExactStaticMethod(TestBean.class, "bar", NumberUtils.LONG_ONE));
915         assertThrows(
916                 NoSuchMethodException.class,
917                 () -> MethodUtils.invokeExactStaticMethod(TestBean.class, "bar", Boolean.TRUE));
918     }
919 
920     @Test
921     public void testInvokeJavaVarargsOverloadingResolution() throws Exception {
922         assertEquals("Byte...", MethodUtils.invokeStaticMethod(TestBean.class,
923                 "varOverload", (byte) 1, (byte) 2));
924         assertEquals("Short...", MethodUtils.invokeStaticMethod(TestBean.class,
925                 "varOverload", (short) 1, (short) 2));
926         assertEquals("Integer...", MethodUtils.invokeStaticMethod(TestBean.class,
927                 "varOverload", 1, 2));
928         assertEquals("Long...", MethodUtils.invokeStaticMethod(TestBean.class,
929                 "varOverload", 1L, 2L));
930         assertEquals("Float...", MethodUtils.invokeStaticMethod(TestBean.class,
931                 "varOverload", 1f, 2f));
932         assertEquals("Double...", MethodUtils.invokeStaticMethod(TestBean.class,
933                 "varOverload", 1d, 2d));
934         assertEquals("Character...", MethodUtils.invokeStaticMethod(TestBean.class,
935                 "varOverload", 'a', 'b'));
936         assertEquals("String...", MethodUtils.invokeStaticMethod(TestBean.class,
937                 "varOverload", "a", "b"));
938         assertEquals("Boolean...", MethodUtils.invokeStaticMethod(TestBean.class,
939                 "varOverload", true, false));
940 
941         assertEquals("Object...", MethodUtils.invokeStaticMethod(TestBean.class,
942                 "varOverload", 1, "s"));
943         assertEquals("Object...", MethodUtils.invokeStaticMethod(TestBean.class,
944                 "varOverload", 1, true));
945         assertEquals("Object...", MethodUtils.invokeStaticMethod(TestBean.class,
946                 "varOverload", 1.1, true));
947         assertEquals("Object...", MethodUtils.invokeStaticMethod(TestBean.class,
948                 "varOverload", 'c', true));
949         assertEquals("Number...", MethodUtils.invokeStaticMethod(TestBean.class,
950                 "varOverload", 1, 1.1));
951         assertEquals("Number...", MethodUtils.invokeStaticMethod(TestBean.class,
952                 "varOverload", 1, 1L));
953         assertEquals("Number...", MethodUtils.invokeStaticMethod(TestBean.class,
954                 "varOverload", 1d, 1f));
955         assertEquals("Number...", MethodUtils.invokeStaticMethod(TestBean.class,
956                 "varOverload", (short) 1, (byte) 1));
957         assertEquals("Object...", MethodUtils.invokeStaticMethod(TestBean.class,
958                 "varOverload", 1, 'c'));
959         assertEquals("Object...", MethodUtils.invokeStaticMethod(TestBean.class,
960                 "varOverload", 'c', "s"));
961 
962         assertEquals("Object...", MethodUtils.invokeStaticMethod(TestBean.class, "varOverload",
963                 (Object[]) ArrayUtils.EMPTY_CLASS_ARRAY));
964         assertEquals("Number...", MethodUtils.invokeStaticMethod(TestBean.class, "numOverload",
965                 (Object[]) ArrayUtils.EMPTY_CLASS_ARRAY));
966     }
967 
968     @Test
969     public void testInvokeMethod() throws Exception {
970         assertEquals("foo()", MethodUtils.invokeMethod(testBean, "foo",
971                 (Object[]) ArrayUtils.EMPTY_CLASS_ARRAY));
972         assertEquals("foo()", MethodUtils.invokeMethod(testBean, "foo"));
973         assertEquals("foo()", MethodUtils.invokeMethod(testBean, "foo",
974                 (Object[]) null));
975         assertEquals("foo()", MethodUtils.invokeMethod(testBean, "foo",
976                 null, null));
977         assertEquals("foo(String)", MethodUtils.invokeMethod(testBean, "foo",
978                 ""));
979         assertEquals("foo(Object)", MethodUtils.invokeMethod(testBean, "foo",
980                 new Object()));
981         assertEquals("foo(Object)", MethodUtils.invokeMethod(testBean, "foo",
982                 Boolean.TRUE));
983         assertEquals("foo(Integer)", MethodUtils.invokeMethod(testBean, "foo",
984                 NumberUtils.INTEGER_ONE));
985         assertEquals("foo(int)", MethodUtils.invokeMethod(testBean, "foo",
986                 NumberUtils.BYTE_ONE));
987         assertEquals("foo(long)", MethodUtils.invokeMethod(testBean, "foo",
988                 NumberUtils.LONG_ONE));
989         assertEquals("foo(double)", MethodUtils.invokeMethod(testBean, "foo",
990                 NumberUtils.DOUBLE_ONE));
991         assertEquals("foo(String...)", MethodUtils.invokeMethod(testBean, "foo",
992                 "a", "b", "c"));
993         assertEquals("foo(String...)", MethodUtils.invokeMethod(testBean, "foo",
994                 "a", "b", "c"));
995         assertEquals("foo(int, String...)", MethodUtils.invokeMethod(testBean, "foo",
996                 5, "a", "b", "c"));
997         assertEquals("foo(long...)", MethodUtils.invokeMethod(testBean, "foo",
998                 1L, 2L));
999 
1000         assertThrows(NoSuchMethodException.class, () -> MethodUtils.invokeMethod(testBean, "foo", 1, 2));
1001 
1002         TestBean.verify(new ImmutablePair<>("String...", new String[]{"x", "y"}),
1003                 MethodUtils.invokeMethod(testBean, "varOverloadEcho", "x", "y"));
1004         TestBean.verify(new ImmutablePair<>("Number...", new Number[]{17, 23, 42}),
1005                 MethodUtils.invokeMethod(testBean, "varOverloadEcho", 17, 23, 42));
1006         TestBean.verify(new ImmutablePair<>("String...", new String[]{"x", "y"}),
1007                 MethodUtils.invokeMethod(testBean, "varOverloadEcho", "x", "y"));
1008         TestBean.verify(new ImmutablePair<>("Number...", new Number[]{17, 23, 42}),
1009                 MethodUtils.invokeMethod(testBean, "varOverloadEcho", 17, 23, 42));
1010 
1011         assertThrows(NullPointerException.class, () -> MethodUtils.invokeMethod(null, "foo", 1, 2));
1012         assertThrows(NullPointerException.class, () -> MethodUtils.invokeMethod(testBean, null, 1, 2));
1013     }
1014 
1015     @Test
1016     public void testInvokeMethod_VarArgsNotUniqueResolvable() throws Exception {
1017       assertEquals("Boolean...", MethodUtils.invokeMethod(testBean, "varOverload",
1018                                                          new Object[] {null}));
1019       assertEquals("Object...", MethodUtils.invokeMethod(testBean, "varOverload",
1020                                                          (Object[]) null));
1021     }
1022 
1023     @Test
1024     public void testInvokeMethod_VarArgsWithNullValues() throws Exception {
1025         assertEquals("String...", MethodUtils.invokeMethod(testBean, "varOverload",
1026                 "a", null, "c"));
1027         assertEquals("String...", MethodUtils.invokeMethod(testBean, "varOverload",
1028                                                                 "a", "b", null));
1029     }
1030 
1031     @Test
1032     public void testInvokeMethodForceAccessNoArgs() throws Exception {
1033         assertEquals("privateStringStuff()", MethodUtils.invokeMethod(testBean, true, "privateStringStuff"));
1034     }
1035 
1036     @Test
1037     public void testInvokeMethodForceAccessWithArgs() throws Exception {
1038         assertEquals("privateStringStuff(Integer)", MethodUtils.invokeMethod(testBean, true, "privateStringStuff", 5));
1039         assertEquals("privateStringStuff(double)", MethodUtils.invokeMethod(testBean, true, "privateStringStuff", 5.0d));
1040         assertEquals("privateStringStuff(String)", MethodUtils.invokeMethod(testBean, true, "privateStringStuff", "Hi There"));
1041         assertEquals("privateStringStuff(Object)", MethodUtils.invokeMethod(testBean, true, "privateStringStuff", new Date()));
1042 
1043         assertThrows(NullPointerException.class,
1044                 () -> MethodUtils.invokeMethod(null, true, "privateStringStuff", "Hi There"));
1045         assertThrows(NullPointerException.class,
1046                 () -> MethodUtils.invokeMethod(testBean, true, null, "Hi There"));
1047     }
1048 
1049     @Test
1050     public void testInvokeStaticMethod() throws Exception {
1051         assertEquals("bar()", MethodUtils.invokeStaticMethod(TestBean.class,
1052                 "bar", (Object[]) ArrayUtils.EMPTY_CLASS_ARRAY));
1053         assertEquals("bar()", MethodUtils.invokeStaticMethod(TestBean.class,
1054                 "bar", (Object[]) null));
1055         assertEquals("bar()", MethodUtils.invokeStaticMethod(TestBean.class,
1056                 "bar", null, null));
1057         assertEquals("bar(String)", MethodUtils.invokeStaticMethod(
1058                 TestBean.class, "bar", ""));
1059         assertEquals("bar(Object)", MethodUtils.invokeStaticMethod(
1060                 TestBean.class, "bar", new Object()));
1061         assertEquals("bar(Object)", MethodUtils.invokeStaticMethod(
1062                 TestBean.class, "bar", Boolean.TRUE));
1063         assertEquals("bar(Integer)", MethodUtils.invokeStaticMethod(
1064                 TestBean.class, "bar", NumberUtils.INTEGER_ONE));
1065         assertEquals("bar(int)", MethodUtils.invokeStaticMethod(TestBean.class,
1066                 "bar", NumberUtils.BYTE_ONE));
1067         assertEquals("bar(double)", MethodUtils.invokeStaticMethod(
1068                 TestBean.class, "bar", NumberUtils.DOUBLE_ONE));
1069         assertEquals("bar(String...)", MethodUtils.invokeStaticMethod(
1070                 TestBean.class, "bar", "a", "b"));
1071         assertEquals("bar(long...)", MethodUtils.invokeStaticMethod(
1072                 TestBean.class, "bar", 1L, 2L));
1073         assertEquals("bar(int, String...)", MethodUtils.invokeStaticMethod(
1074                 TestBean.class, "bar", NumberUtils.INTEGER_ONE, "a", "b"));
1075 
1076         TestBean.verify(new ImmutablePair<>("String...", new String[]{"x", "y"}),
1077                 MethodUtils.invokeStaticMethod(TestBean.class, "varOverloadEchoStatic", "x", "y"));
1078         TestBean.verify(new ImmutablePair<>("Number...", new Number[]{17, 23, 42}),
1079                 MethodUtils.invokeStaticMethod(TestBean.class, "varOverloadEchoStatic", 17, 23, 42));
1080         TestBean.verify(new ImmutablePair<>("String...", new String[]{"x", "y"}),
1081                 MethodUtils.invokeStaticMethod(TestBean.class, "varOverloadEchoStatic", "x", "y"));
1082         TestBean.verify(new ImmutablePair<>("Number...", new Number[]{17, 23, 42}),
1083                 MethodUtils.invokeStaticMethod(TestBean.class, "varOverloadEchoStatic", 17, 23, 42));
1084 
1085         assertThrows(
1086                 NoSuchMethodException.class, () -> MethodUtils.invokeStaticMethod(TestBean.class, "does_not_exist"));
1087     }
1088 
1089     @Test
1090     public void testNullArgument() {
1091         expectMatchingAccessibleMethodParameterTypes(TestBean.class, "oneParameter",
1092                 singletonArray(null), singletonArray(String.class));
1093     }
1094 
1095     @Test
1096     public void testVarArgsUnboxing() throws Exception {
1097         final TestBean testBean = new TestBean();
1098         final int[] actual = (int[]) MethodUtils.invokeMethod(testBean, "unboxing", Integer.valueOf(1), Integer.valueOf(2));
1099         assertArrayEquals(new int[]{1, 2}, actual);
1100     }
1101 
1102     private String toString(final Class<?>[] c) {
1103         return Arrays.asList(c).toString();
1104     }
1105 
1106     @Test
1107     public void verifyJavaVarargsOverloadingResolution() {
1108         // This code is not a test of MethodUtils.
1109         // Rather it makes explicit the behavior of the Java specification for
1110         // various cases of overload resolution.
1111         assertEquals("Byte...", TestBean.varOverload((byte) 1, (byte) 2));
1112         assertEquals("Short...", TestBean.varOverload((short) 1, (short) 2));
1113         assertEquals("Integer...", TestBean.varOverload(1, 2));
1114         assertEquals("Long...", TestBean.varOverload(1L, 2L));
1115         assertEquals("Float...", TestBean.varOverload(1f, 2f));
1116         assertEquals("Double...", TestBean.varOverload(1d, 2d));
1117         assertEquals("Character...", TestBean.varOverload('a', 'b'));
1118         assertEquals("String...", TestBean.varOverload("a", "b"));
1119         assertEquals("Boolean...", TestBean.varOverload(true, false));
1120 
1121         assertEquals("Object...", TestBean.varOverload(1, "s"));
1122         assertEquals("Object...", TestBean.varOverload(1, true));
1123         assertEquals("Object...", TestBean.varOverload(1.1, true));
1124         assertEquals("Object...", TestBean.varOverload('c', true));
1125         assertEquals("Number...", TestBean.varOverload(1, 1.1));
1126         assertEquals("Number...", TestBean.varOverload(1, 1L));
1127         assertEquals("Number...", TestBean.varOverload(1d, 1f));
1128         assertEquals("Number...", TestBean.varOverload((short) 1, (byte) 1));
1129         assertEquals("Object...", TestBean.varOverload(1, 'c'));
1130         assertEquals("Object...", TestBean.varOverload('c', "s"));
1131     }
1132 }