1/*
2 * Copyright (C) 2012 The Guava Authors
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.google.common.reflect;
18
19import com.google.common.collect.ImmutableList;
20import com.google.common.collect.Iterables;
21import com.google.common.testing.EqualsTester;
22import com.google.common.testing.NullPointerTester;
23
24import junit.framework.TestCase;
25
26import java.lang.annotation.Retention;
27import java.lang.annotation.RetentionPolicy;
28import java.lang.reflect.Constructor;
29import java.lang.reflect.Method;
30import java.lang.reflect.ParameterizedType;
31import java.lang.reflect.TypeVariable;
32import java.util.Collections;
33
34import javax.annotation.Nullable;
35
36/**
37 * Unit tests for {@link Invokable}.
38 *
39 * @author Ben Yu
40 */
41public class InvokableTest extends TestCase {
42
43  public void testConstructor_returnType() throws Exception {
44    assertEquals(Prepender.class,
45        Prepender.constructor().getReturnType().getType());
46  }
47
48  private static class WithConstructorAndTypeParameter<T> {
49    @SuppressWarnings("unused") // by reflection
50    <X> WithConstructorAndTypeParameter() {}
51  }
52
53  public void testConstructor_returnType_hasTypeParameter() throws Exception {
54    @SuppressWarnings("rawtypes") // Foo.class for Foo<T> is always raw type
55    Class<WithConstructorAndTypeParameter> type = WithConstructorAndTypeParameter.class;
56    @SuppressWarnings("rawtypes") // Foo.class
57    Constructor<WithConstructorAndTypeParameter> constructor = type.getDeclaredConstructor();
58    Invokable<?, ?> factory = Invokable.from(constructor);
59    assertEquals(2, factory.getTypeParameters().length);
60    assertEquals(type.getTypeParameters()[0], factory.getTypeParameters()[0]);
61    assertEquals(constructor.getTypeParameters()[0], factory.getTypeParameters()[1]);
62    ParameterizedType returnType = (ParameterizedType) factory.getReturnType().getType();
63    assertEquals(type, returnType.getRawType());
64    assertEquals(ImmutableList.copyOf(type.getTypeParameters()),
65        ImmutableList.copyOf(returnType.getActualTypeArguments()));
66  }
67
68  public void testConstructor_exceptionTypes() throws Exception {
69    assertEquals(ImmutableList.of(TypeToken.of(NullPointerException.class)),
70        Prepender.constructor(String.class, int.class).getExceptionTypes());
71  }
72
73  public void testConstructor_typeParameters() throws Exception {
74    TypeVariable<?>[] variables =
75        Prepender.constructor().getTypeParameters();
76    assertEquals(1, variables.length);
77    assertEquals("A", variables[0].getName());
78  }
79
80  public void testConstructor_parameters() throws Exception {
81    Invokable<?, Prepender> delegate = Prepender.constructor(String.class, int.class);
82    ImmutableList<Parameter> parameters = delegate.getParameters();
83    assertEquals(2, parameters.size());
84    assertEquals(String.class, parameters.get(0).getType().getType());
85    assertTrue(parameters.get(0).isAnnotationPresent(NotBlank.class));
86    assertEquals(int.class, parameters.get(1).getType().getType());
87    assertFalse(parameters.get(1).isAnnotationPresent(NotBlank.class));
88    new EqualsTester()
89        .addEqualityGroup(parameters.get(0))
90        .addEqualityGroup(parameters.get(1))
91        .testEquals();
92  }
93
94  public void testConstructor_call() throws Exception {
95    Invokable<?, Prepender> delegate = Prepender.constructor(String.class, int.class);
96    Prepender prepender = delegate.invoke(null, "a", 1);
97    assertEquals("a", prepender.prefix);
98    assertEquals(1, prepender.times);
99  }
100
101  public void testConstructor_returning() throws Exception {
102    Invokable<?, Prepender> delegate = Prepender.constructor(String.class, int.class)
103        .returning(Prepender.class);
104    Prepender prepender = delegate.invoke(null, "a", 1);
105    assertEquals("a", prepender.prefix);
106    assertEquals(1, prepender.times);
107  }
108
109  public void testConstructor_invalidReturning() throws Exception {
110    Invokable<?, Prepender> delegate = Prepender.constructor(String.class, int.class);
111    try {
112      delegate.returning(SubPrepender.class);
113      fail();
114    } catch (IllegalArgumentException expected) {}
115  }
116
117  public void testStaticMethod_returnType() throws Exception {
118    Invokable<?, ?> delegate = Prepender.method("prepend", String.class, Iterable.class);
119    assertEquals(new TypeToken<Iterable<String>>() {}, delegate.getReturnType());
120  }
121
122  public void testStaticMethod_exceptionTypes() throws Exception {
123    Invokable<?, ?> delegate = Prepender.method("prepend", String.class, Iterable.class);
124    assertEquals(ImmutableList.of(), delegate.getExceptionTypes());
125  }
126
127  public void testStaticMethod_typeParameters() throws Exception {
128    Invokable<?, ?> delegate = Prepender.method("prepend", String.class, Iterable.class);
129    TypeVariable<?>[] variables = delegate.getTypeParameters();
130    assertEquals(1, variables.length);
131    assertEquals("T", variables[0].getName());
132  }
133
134  public void testStaticMethod_parameters() throws Exception {
135    Invokable<?, ?> delegate = Prepender.method("prepend", String.class, Iterable.class);
136    ImmutableList<Parameter> parameters = delegate.getParameters();
137    assertEquals(2, parameters.size());
138    assertEquals(String.class, parameters.get(0).getType().getType());
139    assertTrue(parameters.get(0).isAnnotationPresent(NotBlank.class));
140    assertEquals(new TypeToken<Iterable<String>>() {}, parameters.get(1).getType());
141    assertFalse(parameters.get(1).isAnnotationPresent(NotBlank.class));
142    new EqualsTester()
143        .addEqualityGroup(parameters.get(0))
144        .addEqualityGroup(parameters.get(1))
145        .testEquals();
146  }
147
148  public void testStaticMethod_call() throws Exception {
149    Invokable<?, ?> delegate = Prepender.method("prepend", String.class, Iterable.class);
150    @SuppressWarnings("unchecked") // prepend() returns Iterable<String>
151    Iterable<String> result = (Iterable<String>)
152        delegate.invoke(null, "a", ImmutableList.of("b", "c"));
153    assertEquals(ImmutableList.of("a", "b", "c"), ImmutableList.copyOf(result));
154  }
155
156  public void testStaticMethod_returning() throws Exception {
157    Invokable<?, Iterable<String>> delegate = Prepender.method(
158            "prepend", String.class, Iterable.class)
159        .returning(new TypeToken<Iterable<String>>() {});
160    assertEquals(new TypeToken<Iterable<String>>() {}, delegate.getReturnType());
161    Iterable<String> result = delegate.invoke(null, "a", ImmutableList.of("b", "c"));
162    assertEquals(ImmutableList.of("a", "b", "c"), ImmutableList.copyOf(result));
163  }
164
165  public void testStaticMethod_returningRawType() throws Exception {
166    @SuppressWarnings("rawtypes") // the purpose is to test raw type
167    Invokable<?, Iterable> delegate = Prepender.method(
168            "prepend", String.class, Iterable.class)
169        .returning(Iterable.class);
170    assertEquals(new TypeToken<Iterable<String>>() {}, delegate.getReturnType());
171    @SuppressWarnings("unchecked") // prepend() returns Iterable<String>
172    Iterable<String> result = delegate.invoke(null, "a", ImmutableList.of("b", "c"));
173    assertEquals(ImmutableList.of("a", "b", "c"), ImmutableList.copyOf(result));
174  }
175
176  public void testStaticMethod_invalidReturning() throws Exception {
177    Invokable<?, Object> delegate = Prepender.method("prepend", String.class, Iterable.class);
178    try {
179      delegate.returning(new TypeToken<Iterable<Integer>>() {});
180      fail();
181    } catch (IllegalArgumentException expected) {}
182  }
183
184  public void testInstanceMethod_returnType() throws Exception {
185    Invokable<?, ?> delegate = Prepender.method("prepend", Iterable.class);
186    assertEquals(new TypeToken<Iterable<String>>() {}, delegate.getReturnType());
187  }
188
189  public void testInstanceMethod_exceptionTypes() throws Exception {
190    Invokable<?, ?> delegate = Prepender.method("prepend", Iterable.class);
191    assertEquals(
192        ImmutableList.of(
193            TypeToken.of(IllegalArgumentException.class),
194            TypeToken.of(NullPointerException.class)),
195        delegate.getExceptionTypes());
196  }
197
198  public void testInstanceMethod_typeParameters() throws Exception {
199    Invokable<?, ?> delegate = Prepender.method("prepend", Iterable.class);
200    assertEquals(0, delegate.getTypeParameters().length);
201  }
202
203  public void testInstanceMethod_parameters() throws Exception {
204    Invokable<?, ?> delegate = Prepender.method("prepend", Iterable.class);
205    ImmutableList<Parameter> parameters = delegate.getParameters();
206    assertEquals(1, parameters.size());
207    assertEquals(new TypeToken<Iterable<String>>() {}, parameters.get(0).getType());
208    assertEquals(0, parameters.get(0).getAnnotations().length);
209    new EqualsTester()
210        .addEqualityGroup(parameters.get(0))
211        .testEquals();
212  }
213
214  public void testInstanceMethod_call() throws Exception {
215    Invokable<Prepender, ?> delegate = Prepender.method("prepend", Iterable.class);
216    @SuppressWarnings("unchecked") // prepend() returns Iterable<String>
217    Iterable<String> result = (Iterable<String>)
218        delegate.invoke(new Prepender("a", 2), ImmutableList.of("b", "c"));
219    assertEquals(ImmutableList.of("a", "a", "b", "c"), ImmutableList.copyOf(result));
220  }
221
222  public void testInstanceMethod_returning() throws Exception {
223    Invokable<Prepender, Iterable<String>> delegate = Prepender.method(
224            "prepend", Iterable.class)
225        .returning(new TypeToken<Iterable<String>>() {});
226    assertEquals(new TypeToken<Iterable<String>>() {}, delegate.getReturnType());
227    Iterable<String> result = delegate.invoke(new Prepender("a", 2), ImmutableList.of("b", "c"));
228    assertEquals(ImmutableList.of("a", "a", "b", "c"), ImmutableList.copyOf(result));
229  }
230
231  public void testInstanceMethod_returningRawType() throws Exception {
232    @SuppressWarnings("rawtypes") // the purpose is to test raw type
233    Invokable<Prepender, Iterable> delegate = Prepender.method("prepend", Iterable.class)
234        .returning(Iterable.class);
235    assertEquals(new TypeToken<Iterable<String>>() {}, delegate.getReturnType());
236    @SuppressWarnings("unchecked") // prepend() returns Iterable<String>
237    Iterable<String> result = delegate.invoke(
238        new Prepender("a", 2), ImmutableList.of("b", "c"));
239    assertEquals(ImmutableList.of("a", "a", "b", "c"), ImmutableList.copyOf(result));
240  }
241
242  public void testInstanceMethod_invalidReturning() throws Exception {
243    Invokable<?, Object> delegate = Prepender.method("prepend", Iterable.class);
244    try {
245      delegate.returning(new TypeToken<Iterable<Integer>>() {});
246      fail();
247    } catch (IllegalArgumentException expected) {}
248  }
249
250  public void testPrivateInstanceMethod_isOverridable() throws Exception {
251    Invokable<?, ?> delegate = Prepender.method("privateMethod");
252    assertTrue(delegate.isPrivate());
253    assertFalse(delegate.isOverridable());
254    assertFalse(delegate.isVarArgs());
255  }
256
257  public void testPrivateFinalInstanceMethod_isOverridable() throws Exception {
258    Invokable<?, ?> delegate = Prepender.method("privateFinalMethod");
259    assertTrue(delegate.isPrivate());
260    assertTrue(delegate.isFinal());
261    assertFalse(delegate.isOverridable());
262    assertFalse(delegate.isVarArgs());
263  }
264
265  public void testStaticMethod_isOverridable() throws Exception {
266    Invokable<?, ?> delegate = Prepender.method("staticMethod");
267    assertTrue(delegate.isStatic());
268    assertFalse(delegate.isOverridable());
269    assertFalse(delegate.isVarArgs());
270  }
271
272  public void testStaticFinalMethod_isFinal() throws Exception {
273    Invokable<?, ?> delegate = Prepender.method("staticFinalMethod");
274    assertTrue(delegate.isStatic());
275    assertTrue(delegate.isFinal());
276    assertFalse(delegate.isOverridable());
277    assertFalse(delegate.isVarArgs());
278  }
279
280  static class Foo {}
281
282  public void testConstructor_isOverridablel() throws Exception {
283    Invokable<?, ?> delegate = Invokable.from(Foo.class.getDeclaredConstructor());
284    assertFalse(delegate.isOverridable());
285    assertFalse(delegate.isVarArgs());
286  }
287
288  public void testMethod_isVarArgs() throws Exception {
289    Invokable<?, ?> delegate = Prepender.method("privateVarArgsMethod", String[].class);
290    assertTrue(delegate.isVarArgs());
291  }
292
293  public void testConstructor_isVarArgs() throws Exception {
294    Invokable<?, ?> delegate = Prepender.constructor(String[].class);
295    assertTrue(delegate.isVarArgs());
296  }
297
298  public void testGetOwnerType_constructor() throws Exception {
299    Invokable<String, String> invokable = Invokable.from(String.class.getConstructor());
300    assertEquals(TypeToken.of(String.class), invokable.getOwnerType());
301  }
302
303  public void testGetOwnerType_method() throws Exception {
304    Invokable<?, ?> invokable = Invokable.from(String.class.getMethod("length"));
305    assertEquals(TypeToken.of(String.class), invokable.getOwnerType());
306  }
307
308  private static final class FinalClass {
309    @SuppressWarnings("unused") // used by reflection
310    void notFinalMethod() {}
311  }
312
313  public void testNonFinalMethodInFinalClass_isOverridable() throws Exception {
314    Invokable<?, ?> delegate = Invokable.from(
315        FinalClass.class.getDeclaredMethod("notFinalMethod"));
316    assertFalse(delegate.isOverridable());
317    assertFalse(delegate.isVarArgs());
318  }
319
320  private class InnerWithDefaultConstructor {
321    class NestedInner {}
322  }
323
324  public void testInnerClassDefaultConstructor() {
325    Constructor<?> constructor =
326        InnerWithDefaultConstructor.class.getDeclaredConstructors() [0];
327    assertEquals(0, Invokable.from(constructor).getParameters().size());
328  }
329
330  public void testNestedInnerClassDefaultConstructor() {
331    Constructor<?> constructor =
332        InnerWithDefaultConstructor.NestedInner.class.getDeclaredConstructors() [0];
333    assertEquals(0, Invokable.from(constructor).getParameters().size());
334  }
335
336  private class InnerWithOneParameterConstructor {
337    @SuppressWarnings("unused") // called by reflection
338    public InnerWithOneParameterConstructor(String s) {}
339  }
340
341  public void testInnerClassWithOneParameterConstructor() {
342    Constructor<?> constructor =
343        InnerWithOneParameterConstructor.class.getDeclaredConstructors()[0];
344    Invokable<?, ?> invokable = Invokable.from(constructor);
345    assertEquals(1, invokable.getParameters().size());
346    assertEquals(TypeToken.of(String.class), invokable.getParameters().get(0).getType());
347  }
348
349  private class InnerWithAnnotatedConstructorParameter {
350    @SuppressWarnings("unused") // called by reflection
351    InnerWithAnnotatedConstructorParameter(@Nullable String s) {}
352  }
353
354  public void testInnerClassWithAnnotatedConstructorParameter() {
355    Constructor<?> constructor =
356        InnerWithAnnotatedConstructorParameter.class.getDeclaredConstructors() [0];
357    Invokable<?, ?> invokable = Invokable.from(constructor);
358    assertEquals(1, invokable.getParameters().size());
359    assertEquals(TypeToken.of(String.class), invokable.getParameters().get(0).getType());
360  }
361
362  private class InnerWithGenericConstructorParameter {
363    @SuppressWarnings("unused") // called by reflection
364    InnerWithGenericConstructorParameter(Iterable<String> it, String s) {}
365  }
366
367  public void testInnerClassWithGenericConstructorParameter() {
368    Constructor<?> constructor =
369        InnerWithGenericConstructorParameter.class.getDeclaredConstructors() [0];
370    Invokable<?, ?> invokable = Invokable.from(constructor);
371    assertEquals(2, invokable.getParameters().size());
372    assertEquals(new TypeToken<Iterable<String>>() {},
373        invokable.getParameters().get(0).getType());
374    assertEquals(TypeToken.of(String.class),
375        invokable.getParameters().get(1).getType());
376  }
377
378  public void testAnonymousClassDefaultConstructor() {
379    final int i = 1;
380    final String s = "hello world";
381    Class<?> anonymous = new Runnable() {
382      @Override public void run() {
383        System.out.println(s + i);
384      }
385    }.getClass();
386    Constructor<?> constructor = anonymous.getDeclaredConstructors() [0];
387    assertEquals(0, Invokable.from(constructor).getParameters().size());
388  }
389
390  public void testAnonymousClassWithTwoParametersConstructor() {
391    abstract class Base {
392      @SuppressWarnings("unused") // called by reflection
393      Base(String s, int i) {}
394    }
395    Class<?> anonymous = new Base("test", 0) {}.getClass();
396    Constructor<?> constructor = anonymous.getDeclaredConstructors() [0];
397    assertEquals(2, Invokable.from(constructor).getParameters().size());
398  }
399
400  public void testLocalClassDefaultConstructor() {
401    final int i = 1;
402    final String s = "hello world";
403    class LocalWithDefaultConstructor implements Runnable {
404      @Override public void run() {
405        System.out.println(s + i);
406      }
407    }
408    Constructor<?> constructor = LocalWithDefaultConstructor.class.getDeclaredConstructors() [0];
409    assertEquals(0, Invokable.from(constructor).getParameters().size());
410  }
411
412  public void testStaticAnonymousClassDefaultConstructor() throws Exception {
413    doTestStaticAnonymousClassDefaultConstructor();
414  }
415
416  private static void doTestStaticAnonymousClassDefaultConstructor() {
417    final int i = 1;
418    final String s = "hello world";
419    Class<?> anonymous = new Runnable() {
420      @Override public void run() {
421        System.out.println(s + i);
422      }
423    }.getClass();
424    Constructor<?> constructor = anonymous.getDeclaredConstructors() [0];
425    assertEquals(0, Invokable.from(constructor).getParameters().size());
426  }
427
428  public void testAnonymousClassInConstructor() {
429    new AnonymousClassInConstructor();
430  }
431
432  private static class AnonymousClassInConstructor {
433    AnonymousClassInConstructor() {
434      final int i = 1;
435      final String s = "hello world";
436      Class<?> anonymous = new Runnable() {
437        @Override public void run() {
438          System.out.println(s + i);
439        }
440      }.getClass();
441      Constructor<?> constructor = anonymous.getDeclaredConstructors() [0];
442      assertEquals(0, Invokable.from(constructor).getParameters().size());
443    }
444  }
445
446  public void testLocalClassInInstanceInitializer() {
447    new LocalClassInInstanceInitializer();
448  }
449
450  private static class LocalClassInInstanceInitializer {
451    {
452      class Local {}
453      Constructor<?> constructor = Local.class.getDeclaredConstructors() [0];
454      assertEquals(0, Invokable.from(constructor).getParameters().size());
455    }
456  }
457
458  public void testLocalClassInStaticInitializer() {
459    new LocalClassInStaticInitializer();
460  }
461
462  private static class LocalClassInStaticInitializer {
463    static {
464      class Local {}
465      Constructor<?> constructor = Local.class.getDeclaredConstructors() [0];
466      assertEquals(0, Invokable.from(constructor).getParameters().size());
467    }
468  }
469
470  public void testLocalClassWithSeeminglyHiddenThisInStaticInitializer_BUG() {
471    new LocalClassWithSeeminglyHiddenThisInStaticInitializer();
472  }
473
474  /**
475   * This class demonstrates a bug in getParameters() when the local class is inside static
476   * initializer.
477   */
478  private static class LocalClassWithSeeminglyHiddenThisInStaticInitializer {
479    static {
480      class Local {
481        @SuppressWarnings("unused") // through reflection
482        Local(LocalClassWithSeeminglyHiddenThisInStaticInitializer outer) {}
483      }
484      Constructor<?> constructor = Local.class.getDeclaredConstructors() [0];
485      int miscalculated = 0;
486      assertEquals(miscalculated, Invokable.from(constructor).getParameters().size());
487    }
488  }
489
490  public void testLocalClassWithOneParameterConstructor() throws Exception {
491    final int i = 1;
492    final String s = "hello world";
493    class LocalWithOneParameterConstructor {
494      @SuppressWarnings("unused") // called by reflection
495      public LocalWithOneParameterConstructor(String x) {
496        System.out.println(s + i);
497      }
498    }
499    Constructor<?> constructor =
500        LocalWithOneParameterConstructor.class.getDeclaredConstructors()[0];
501    Invokable<?, ?> invokable = Invokable.from(constructor);
502    assertEquals(1, invokable.getParameters().size());
503    assertEquals(TypeToken.of(String.class), invokable.getParameters().get(0).getType());
504  }
505
506  public void testLocalClassWithAnnotatedConstructorParameter() throws Exception {
507    class LocalWithAnnotatedConstructorParameter {
508      @SuppressWarnings("unused") // called by reflection
509      LocalWithAnnotatedConstructorParameter(@Nullable String s) {}
510    }
511    Constructor<?> constructor =
512        LocalWithAnnotatedConstructorParameter.class.getDeclaredConstructors() [0];
513    Invokable<?, ?> invokable = Invokable.from(constructor);
514    assertEquals(1, invokable.getParameters().size());
515    assertEquals(TypeToken.of(String.class), invokable.getParameters().get(0).getType());
516  }
517
518  public void testLocalClassWithGenericConstructorParameter() throws Exception {
519    class LocalWithGenericConstructorParameter {
520      @SuppressWarnings("unused") // called by reflection
521      LocalWithGenericConstructorParameter(Iterable<String> it, String s) {}
522    }
523    Constructor<?> constructor =
524        LocalWithGenericConstructorParameter.class.getDeclaredConstructors() [0];
525    Invokable<?, ?> invokable = Invokable.from(constructor);
526    assertEquals(2, invokable.getParameters().size());
527    assertEquals(new TypeToken<Iterable<String>>() {},
528        invokable.getParameters().get(0).getType());
529    assertEquals(TypeToken.of(String.class),
530        invokable.getParameters().get(1).getType());
531  }
532
533  public void testEquals() throws Exception {
534    new EqualsTester()
535        .addEqualityGroup(Prepender.constructor(), Prepender.constructor())
536        .addEqualityGroup(Prepender.constructor(String.class, int.class))
537        .addEqualityGroup(Prepender.method("privateMethod"), Prepender.method("privateMethod"))
538        .addEqualityGroup(Prepender.method("privateFinalMethod"))
539        .testEquals();
540  }
541
542  public void testNulls() {
543    new NullPointerTester().testAllPublicStaticMethods(Invokable.class);
544    new NullPointerTester().testAllPublicInstanceMethods(Prepender.method("staticMethod"));
545  }
546
547  @Retention(RetentionPolicy.RUNTIME)
548  private @interface NotBlank {}
549
550  /** Class for testing constructor, static method and instance method. */
551  @SuppressWarnings("unused") // most are called by reflection
552  private static class Prepender {
553
554    private final String prefix;
555    private final int times;
556
557    Prepender(@NotBlank String prefix, int times) throws NullPointerException {
558      this.prefix = prefix;
559      this.times = times;
560    }
561
562    Prepender(String... varargs) {
563      this(null, 0);
564    }
565
566    // just for testing
567    private <A> Prepender() {
568      this(null, 0);
569    }
570
571    static <T> Iterable<String> prepend(@NotBlank String first, Iterable<String> tail) {
572      return Iterables.concat(ImmutableList.of(first), tail);
573    }
574
575    Iterable<String> prepend(Iterable<String> tail)
576        throws IllegalArgumentException, NullPointerException {
577      return Iterables.concat(Collections.nCopies(times, prefix), tail);
578    }
579
580    static Invokable<?, Prepender> constructor(Class<?>... parameterTypes) throws Exception {
581      Constructor<Prepender> constructor = Prepender.class.getDeclaredConstructor(parameterTypes);
582      return Invokable.from(constructor);
583    }
584
585    static Invokable<Prepender, Object> method(String name, Class<?>... parameterTypes) {
586      try {
587        Method method = Prepender.class.getDeclaredMethod(name, parameterTypes);
588        @SuppressWarnings("unchecked") // The method is from Prepender.
589        Invokable<Prepender, Object> invokable = (Invokable<Prepender, Object>)
590            Invokable.from(method);
591        return invokable;
592      } catch (NoSuchMethodException e) {
593        throw new IllegalArgumentException(e);
594      }
595    }
596
597    private void privateMethod() {}
598
599    private final void privateFinalMethod() {}
600
601    static void staticMethod() {}
602
603    static final void staticFinalMethod() {}
604
605    private void privateVarArgsMethod(String... varargs) {}
606  }
607
608  private static class SubPrepender extends Prepender {
609    @SuppressWarnings("unused") // needed to satisfy compiler, never called
610    public SubPrepender() throws NullPointerException {
611      throw new AssertionError();
612    }
613  }
614}
615