public VarArgsFunc lookupVisitFunc(
        Class<? extends R> visitorClass,
        Class<? extends E> visiteeClass,
        String visitMethodName,
        List<Class> additionalParameterTypes)
          throws Throwable {
      final List<Object> key =
          ImmutableList.of(
              visitorClass,
              visiteeClass,
              visitMethodName,
              additionalParameterTypes);
      VarArgsFunc method = map.get(key);
      if (method == null) {
        if (map.containsKey(key)) {
          // We already looked for the method and found nothing.
        } else {
          Method method1 =
              ReflectUtil.lookupVisitMethod(
                  visitorClass,
                  visiteeClass,
                  visitMethodName,
                  additionalParameterTypes);
          MethodHandles.Lookup lookup = MethodHandles.lookup();
          MethodHandle methodHandle = lookup.unreflect(method1);
          int argsLength = 1 + method1.getParameterTypes().length;
          MethodType invokedType = MethodType.methodType(
              getVarArgsFuncClass(argsLength));
          MethodType functionMethodType = MethodType.methodType(
              method1.getReturnType(), visitorClass, method1.getParameterTypes());
          CallSite site = LambdaMetafactory.metafactory(
              lookup,
              "apply",
              invokedType,
              functionMethodType.generic(),
              methodHandle,
              methodHandle.type());
          method = getVarArgsFunc(argsLength, site);
          map.put(key, method);
        }
      }
      return method;
    }
Creates a dispatcher for calls to {@link VisitDispatcher#lookupVisitFunc}. The
dispatcher caches methods between invocations and it is thread-safe.

@param visitorBaseClazz Visitor base class
@param visiteeBaseClazz Visitee base class
@return cache of methods
ql/src/java/org/apache/hadoop/hive/ql/optimizer/calcite/rules/HiveReflectUtil.java