private CheckedFunction getMethod(final String predicateName, final Class<?>[] argumentClasses) throws IOException {
        final PFunctionId id = new PFunctionId(predicateName, argumentClasses);

        CheckedFunction<Object[], T> result = methods.get(id);

        if (result == null) {
            boolean collectionType = false;
            Method m;
            try {
                // do a direct lookup
                m = classOfP.getMethod(predicateName, argumentClasses);
            } catch (NoSuchMethodException ex0) {
                // then try collection types
                try {
                    m = classOfP.getMethod(predicateName, Collection.class);
                    collectionType = true;
                } catch (NoSuchMethodException ex1) {
                    // finally go for the generics
                    try {
                        m = classOfP.getMethod(predicateName, Object.class);
                    } catch (NoSuchMethodException ex2) {
                        // finally go for the generics
                        try {
                            m = classOfP.getMethod(predicateName, Object.class, Object.class);
                        } catch (NoSuchMethodException ex3) {
                            throw new IOException(String.format("Can't find predicate method: '%s'", predicateName), ex2);
                        }
                    }
                }
            }

            final Method finalMethod = m;

            try {
                if (Modifier.isStatic(m.getModifiers())) {
                    if (collectionType) {
                        result = (args) -> (T) finalMethod.invoke(null, Arrays.asList(args));
                    } else {
                        result = (args) -> (T) finalMethod.invoke(null, args);
                    }
                } else {
                    // try an instance method as it might be a form of ConnectiveP which means there is a P as an
                    // argument that should be used as the object of an instance method
                    if (argumentClasses.length != 2) {
                        throw new IllegalStateException(String.format("Could not determine the form of P for %s and %s",
                                predicateName, Arrays.asList(argumentClasses)));
                    }

                    result = (args) -> {
                        if (!(args[0] instanceof P) || !(args[1] instanceof P))
                            throw new IllegalStateException(String.format("Could not determine the form of P for %s and %s",
                                    predicateName, Arrays.asList(args)));

                        final P firstP = (P) args[0];
                        final P secondP = (P) args[1];

                        return (T) finalMethod.invoke(firstP, secondP);
                    };
                }

                methods.put(id, result);
            } catch (Exception ex) {
                throw new IOException(ex);
            }
        }

        return result;
    }
@author Stephen Mallette (http://stephen.genoprime.com)
gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/binary/types/PSerializer.java