public static <FnT extends DoFn<?, ?>> DoFnSignature getSignature(Class<FnT> fn) {
    return signatureCache.computeIfAbsent(fn, DoFnSignatures::parseSignature);
  }
@Test
  public void testOnTimerDeclaredInSubclass() throws Exception {
    thrown.expect(IllegalArgumentException.class);
    thrown.expectMessage("Callback");
    thrown.expectMessage("declared in a different class");
    thrown.expectMessage(DoFnWithOnlyCallback.TIMER_ID);
    thrown.expectMessage(not(mentionsState()));
    thrown.expectMessage(mentionsTimers());
    DoFnSignatures.getSignature(
        new DoFnWithOnlyCallback() {

          @TimerId(DoFnWithOnlyCallback.TIMER_ID)
          private final TimerSpec myfield1 = TimerSpecs.timer(TimeDomain.EVENT_TIME);

          @ProcessElement
          @Override
          public void foo(ProcessContext context) {}
        }.getClass());
  }