Search code examples
javawampdeferredtypechecking

Passing Java functions as arguments and typechecking response


I need to pass an arbitrary Java method to another class where it will be executed asynchronously. I have a feeling I can use lambda functions as the parameters of my call method, but I'm not sure if I need to create a functional interface for it. I will also need to typecheck the response.

private Object foo(String method, Object...args){
    try{
        result.set( connection.invoke(method, args) );
    } catch (InterruptedException e) {

    }

    return result.get();
}

I noticed someone wanted to do something similar here, but I need to pass an arbitrary number of arguments (BiConsumer only works for 2). I don't know how many arguments I will need call to accept.

I also need to typecheck the response, and everything I've found so far regarding Java typechecking says this is not possible. Is it?


Solution

  • You can make varargs methods with the following signature:

    interface Invokable extends FunctionalInterface {
        Object invoke(Object... arguments);
    }
    

    Primitive can be passed as argument since they will be auto boxed to their object equivalent (int -> Integer, long -> Long, and so on).

    However, you will be forced to use cast to do type checking.

    What I recommend instead is to use an Objectcontaining the arguments and to parametrize the functional interface:

    interface Argument<R> {
        // Empty interface used to mark argument types.
    }
    
    interface Invokable<R, A extends Argument<R>> extends FunctionalInterface {
        R invoke(A argument);
    }
    

    And then change your foo method to find the method to call depending on the argument class. Like so:

    private <A, R> R foo(A arg) {
        if (arg != null) {
            // TODO: Use injection or a map to create a relation between Class<A> and its Invokable<A, R>.
            final Invokable<A, R> invokable = invokables.get(a.getClass());
            try {
                return invokable.invoke(arg); // Type checked result (R).
            } catch (InterruptedException e) {
                // TODO: Handle exception.
            }
        }
        return null;
    }
    

    This pattern is usually called the Command pattern.