private TestCase(Class<T> rtype, Function<Object, T> cast, ThrowMode throwMode, Throwable thrown)
      throws NoSuchMethodException, IllegalAccessException {
    this.cast = cast;
    filter =
        MethodHandles.lookup()
            .findVirtual(Function.class, "apply", MethodType.methodType(Object.class, Object.class))
            .bindTo(cast);
    this.rtype = rtype;
    this.throwMode = throwMode;
    this.throwableClass = thrown.getClass();
    switch (throwMode) {
      case NOTHING:
        this.thrown = null;
        break;
      case ADAPTER:
      case UNCAUGHT:
        this.thrown = new Error("do not catch this");
        break;
      default:
        this.thrown = thrown;
    }

    MethodHandle throwOrReturn = THROW_OR_RETURN;
    if (throwMode == ThrowMode.ADAPTER) {
      MethodHandle fakeIdentity = FAKE_IDENTITY.bindTo(this);
      for (int i = 0; i < 10; ++i) {
        throwOrReturn = MethodHandles.filterReturnValue(throwOrReturn, fakeIdentity);
      }
    }
    thrower = throwOrReturn.asType(MethodType.genericMethodType(2));
  }
  public static void main(String[] args) throws Throwable {
    Object x, y;
    String s;
    int i;
    MethodType mt;
    MethodHandle mh;
    MethodHandles.Lookup lookup = MethodHandles.lookup();
    // mt is (char,char)String
    mt = MethodType.methodType(String.class, char.class, char.class);
    mh = lookup.findVirtual(String.class, "replace", mt);
    s = (String) mh.invokeExact("daddy", 'd', 'n');
    System.out.println("Result: " + s);
    // invokeExact(Ljava/lang/String;CC)Ljava/lang/String;

    // weakly typed invocation (using MHs.invoke)
    s = (String) mh.invokeWithArguments("sappy", 'p', 'v');
    System.out.println("Result: " + s);
    // mt is (Object[])List

    mt = MethodType.methodType(java.util.List.class, Object[].class);
    mh = lookup.findStatic(java.util.Arrays.class, "asList", mt);
    assert (mh.isVarargsCollector());
    x = mh.invoke("one", "two");
    // ArrayList
    System.out.println("x: " + x + " type: " + x.getClass().getCanonicalName());

    // invoke(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/Object;
    // assertEquals(x, java.util.Arrays.asList("one","two"));

    // mt is (Object,Object,Object)Object
    mt = MethodType.genericMethodType(3);
    mh = mh.asType(mt);
    x = mh.invokeExact((Object) 1, (Object) 2, (Object) 3);
    // invokeExact(Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;
    // 1,2,3
    System.out.println("x: " + x + " type: " + x.getClass().getCanonicalName());

    // assertEquals(x, java.util.Arrays.asList(1,2,3));
    // mt is ()int
    mt = MethodType.methodType(int.class);
    mh = lookup.findVirtual(java.util.List.class, "size", mt);
    i = (int) mh.invokeExact(java.util.Arrays.asList(1, 2, 3));
    // invokeExact(Ljava/util/List;)I
    assert (i == 3);

    mt = MethodType.methodType(void.class, String.class);
    mh = lookup.findVirtual(java.io.PrintStream.class, "println", mt);
    mh.invokeExact(System.out, "Hello, world.");
    // invokeExact(Ljava/io/PrintStream;Ljava/lang/String;)V
  }