Java ExecutorService invokeAll with different types

Viewed 317

I am using Java 1.7.

I am trying to execute two methods concurrently. However, each method has a different return type.

I have the following:

private ExecutorService executorService = Executors.newFixedThreadPool(2);

private void executeConcurrentTasks(final boolean autoBookingEnabled, final TripDTO tripDTO, final ApprovalRequestDetails approvalRequestDetails, final ApprovalRequest approvalRequest, final QuoteResponse quoteResponse, final TripRequirementsResponse tripRequirementsResponse, final List<List<Evaluator>> evaluatorsList, final List<EvaluationApprovalTree> evaluationTree, final Long memberId, final Map<String,Object> properties, final HttpSession session) {

    List<Callable<?>> tasks = Arrays.asList(
        new Callable<BookServicesResponse>() {
            public BookServicesResponse call() throws Exception {
                BookServicesResponse autoBookResponse = bookServices(autoBookingEnabled, tripDTO, approvalRequestDetails, approvalRequest, quoteResponse, memberId, properties, session);
                return autoBookResponse;
            }
        },
        new Callable<ApprovalResponse>() {
            public ApprovalResponse call() throws Exception {
                ApprovalResponse approvalResponse = submitApproval(approvalRequest, tripDTO, tripRequirementsResponse, evaluatorsList, evaluationTree, memberId, properties, session);
                return approvalResponse;
            }
        });

    List<Future<?>> futures = executorService.invokeAll(tasks); // compile error
    
    for (Future<?> future : futures) {
        try {
            Object obj = future.get();
            if (obj instanceof BookServicesResponse) {
                // ...
            } else if (obj instanceof ApprovalResponse) {
                // ...
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }
    }

}

Compile error:

enter image description here

Question

Is there a generic way to handle the call of the two methods, so that the returned list of futures each have a different object?

2 Answers

I don't know if this is the best solution, and it doesn't actually answer the question how to use generics to have a list of different callables. But I ended up using two separate Callables and Futures.

    Callable<BookServicesResponse> taskBook = new Callable<BookServicesResponse>() {
            public BookServicesResponse call() throws Exception {
                BookServicesResponse autoBookResponse = bookServices(autoBookingEnabled, tripDTO, approvalRequestDetails, approvalRequest, quoteResponse, memberId, properties, session);
                return autoBookResponse;
            }
        };

    Callable<ApprovalResponse> taskApprove = new Callable<ApprovalResponse>() {
            public ApprovalResponse call() throws Exception {
                ApprovalResponse approvalResponse = submitApproval(approvalRequest, tripDTO, tripRequirementsResponse, evaluatorsList, evaluationTree, memberId, properties, session);
                return approvalResponse;
            }
        };

    Future<BookServicesResponse> futureBook = executorService.submit(taskBook);
    Future<ApprovalResponse> futureApprove = executorService.submit(taskApprove);

It doesn't work the way you tried because invokeAll expects a parameter of type List<? extends Callable<T>> and not List<? extends Callable<? extends T>>. You could consider this a downside of the API.

In order to get around this, you can use a List<Callable<Object>> or use a common superclass of BookServicdsResponse and ArrovalResponse (I'll use Response in this example) :

List<Callable<Response>> tasks = Arrays.asList(
        new Callable<Response>() {
            public Response call() throws Exception {
                BookServicesResponse autoBookResponse = bookServices(autoBookingEnabled, tripDTO, approvalRequestDetails, approvalRequest, quoteResponse, memberId, properties, session);
                return autoBookResponse;
            }
        },
        new Callable<Response>() {
            public Response call() throws Exception {
                ApprovalResponse approvalResponse = submitApproval(approvalRequest, tripDTO, tripRequirementsResponse, evaluatorsList, evaluationTree, memberId, properties, session);
                return approvalResponse;
            }
        });

    List<Future<Response>> futures = executorService.invokeAll(tasks); // compile error

You can also use an "unsafe" cast like this:

List<Future<?>> futures = executorService.invokeAll((List<Callable<Object>>) tasks);

As you would only retrieve elements from the Callables, this would not cause unwanted ClassCastExceptions.

Related