code: private Method findToStringMethod(Class<?> cls) {
          Method matched = null;
          // find in superclass hierarchy
          Class<?> loopCls = cls;
          while (loopCls != null && matched == null) {
              Method[] methods = loopCls.getDeclaredMethods();
              for (Method method : methods) {
                  if (!method.isBridge() && !method.isSynthetic()) {
                      ToString toString = method.getAnnotation(ToString.class);
                      if (toString != null) {
                          if (matched != null) {
                              throw new IllegalStateException("Two methods are annotated with @ToString: " + cls.getName());
                          }
                          matched = method;
                      }
                  }
              }
              loopCls = loopCls.getSuperclass();
          }
          // find in immediate parent interfaces
          if (matched == null) {
              for (Class<?> loopIfc : eliminateEnumSubclass(cls).getInterfaces()) {
                  Method[] methods = loopIfc.getDeclaredMethods();
                  for (Method method : methods) {
                      if (!method.isBridge() && !method.isSynthetic()) {
                          ToString toString = method.getAnnotation(ToString.class);
                          if (toString != null) {
                              if (matched != null) {
                                  throw new IllegalStateException("Two methods are annotated with @ToString on interfaces: " + cls.getName());
                              }
                              matched = method;
                          }
                      }
                  }
              }
          }
          return matched;
      }
docs: Finds the conversion method.
  
  @param cls  the class to find a method for, not null
  @return the method to call, null means use {@code toString}
  @throws RuntimeException if invalid