View Javadoc
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  
17  package com.google.common.reflect;
18  
19  import com.google.common.collect.ImmutableList;
20  import com.google.common.collect.Iterables;
21  import com.google.common.testing.EqualsTester;
22  import com.google.common.testing.NullPointerTester;
23  
24  import junit.framework.TestCase;
25  
26  import java.lang.annotation.Retention;
27  import java.lang.annotation.RetentionPolicy;
28  import java.lang.reflect.Constructor;
29  import java.lang.reflect.Method;
30  import java.lang.reflect.ParameterizedType;
31  import java.lang.reflect.TypeVariable;
32  import java.util.Collections;
33  
34  import javax.annotation.Nullable;
35  
36  /**
37   * Unit tests for {@link Invokable}.
38   *
39   * @author Ben Yu
40   */
41  public 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 }