-1

I am trying to understand CompletableFuture API, and I got stuck at the following problem.

allOf by itself only returns a CompletableFuture<void>, so it only makes sense to have a slightly better interface with a function called allResultsOf: Such function should be generic, and receive as argument objects of type List<CompletableFuture<T>> and return an object of type CompletableFuture<List<T>>

My attempt at this function declaration was:

public static CompletableFuture<List<T>> allResultsOf(List<CompletableFuture<T>> completableFutures) {
                CompletableFuture<Void> allFutures = CompletableFuture
                .allOf(completableFutures.toArray(new CompletableFuture[completableFutures.size()]));
    
                CompletableFuture<List<T>> allCompletableFuture = allFutures.thenApply(
                    future -> {
                        return completableFutures.stream()
                            .map(completableFuture -> completableFuture.join())
                            .collect(Collectors.toList());
                });
    
                return allCompletableFuture;
            }

However, in my (attached) example snippet, this function gives this compilation error:

error: incompatible types: List<CompletableFuture<Test.GreetHolder>> cannot be converted to List<CompletableFuture<?>> CompletableFuture<List> allCompletableFuture = allResultsOf(completableFutures);

I'm not sure what the problem is here

Example code (extracted from here):

import java.util.*;
import java.util.stream.*;
import java.lang.*;
import java.io.*;
import java.util.concurrent.*;

// The main method must be in a class named "Main".
class Main {
    static class Test {
        private ExecutorService executor;
        public Test(ExecutorService es) {
            executor = es;
        }
        public class GreetHolder {

            private String greet;
        
            public GreetHolder(String greet) {
                this.greet = greet;
            }
        
            public String getGreet() {
                return greet;
            }
        
            public void setGreet(String greet) {
                this.greet = greet;
            }
            
            public String toString() {
                return getGreet();
            }
        }
        
        private CompletableFuture<GreetHolder> getGreeting(String lang) {
            return CompletableFuture.supplyAsync( () -> {
                try {
                    System.out.println("Task execution started for lang =" + lang);
                    Thread.sleep(200);
                    System.out.println("Task execution stopped for lang =" + lang);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                return new GreetHolder(getGreet(lang));
            }, executor);
        }
        public String getGreet(String lang) {
            if (lang.equals("EN")) {
                return "Hello";
            } else if (lang.equals("ES")) {
                return "Hola";
            } else if (lang.equals("SN")) {
                return "Ayubovan";
            } else {
                throw new IllegalArgumentException("Invalid lang param");
            }
        }
        
        public CompletableFuture<List<Test.GreetHolder>> run() {
            List<String>  langList = Arrays.asList("EN", "ES", "SN"); //, "EX");

            List<CompletableFuture<Test.GreetHolder>> completableFutures =
            langList.stream().map(lang -> getGreeting(lang))
                //.map(CompletableFuture::join);
                .collect(Collectors.toList());
            
            // return completableFutures;
            
            CompletableFuture<Void> allFutures = CompletableFuture
            .allOf(completableFutures.toArray(new CompletableFuture[completableFutures.size()]));
            
            CompletableFuture<List<GreetHolder>> allCompletableFuture = allFutures.thenApply(
                future -> {
                    System.out.println(String.format("%s <- future", future));
                    return completableFutures.stream()
                        .map(completableFuture -> completableFuture.join())
                        .collect(Collectors.toList());
            });
            
            System.out.println(String.format("%s <- allCompletableFuture", allCompletableFuture));
            
            //return allCompletableFuture;
            
            
            CompletableFuture completableFuture = allCompletableFuture.thenApply(
                greets -> {
                       return greets
                            .stream()
                            .map(GreetHolder::getGreet)
                            .collect(Collectors.toList());
            });
            return completableFuture;
            
            
        }

        public static CompletableFuture<List<T>> allResultsOf(List<CompletableFuture<T>> completableFutures) {
            CompletableFuture<Void> allFutures = CompletableFuture
            .allOf(completableFutures.toArray(new CompletableFuture[completableFutures.size()]));

            CompletableFuture<List<T>> allCompletableFuture = allFutures.thenApply(
                future -> {
                    System.out.println(String.format("%s <- future", future));
                    return completableFutures.stream()
                        .map(completableFuture -> completableFuture.join())
                        .collect(Collectors.toList());
            });

            return allCompletableFuture;
        }

        public CompletableFuture<List<Test.GreetHolder>> run_tidier() {
            List<String>  langList = Arrays.asList("EN", "ES", "SN"); //, "EX");

            List<CompletableFuture<Test.GreetHolder>> completableFutures =
            langList.stream().map(lang -> getGreeting(lang))
                //.map(CompletableFuture::join);
                .collect(Collectors.toList());
            
            CompletableFuture<List<GreetHolder>> allCompletableFuture = allResultsOf(completableFutures);
            
            System.out.println(String.format("%s <- allCompletableFuture", allCompletableFuture));            
            
            CompletableFuture completableFuture = allCompletableFuture.thenApply(
                greets -> {
                       return greets
                            .stream()
                            .map(GreetHolder::getGreet)
                            .collect(Collectors.toList());
            });
            return completableFuture;
            
            
        }
    }
    public static void main(String[] args) throws InterruptedException, ExecutionException {
        ExecutorService pool = Executors.newFixedThreadPool(2);
        Test t = new Test(pool);
        System.out.println(String.format("%s world!", t.getGreet("EN")));
        CompletableFuture cf = t.run();
        System.out.println(String.format("%s <- cf", cf.get()));
    }
}
lurscher
  • 25,930
  • 29
  • 122
  • 185
  • I think you should not use wildcards for this at all. It makes sense for `allOf` to take whatever futures as it only is about the completion and not the results. The moment when the actual results become relevant, using wildcards becomes inherently wrong. However, that's not the problem you ask about, but the problem is a symptome of it. – akuzminykh Apr 21 '21 at 12:18
  • 1
    In your mind, the `?` in the return and the `?` in the argument are the same type. In the compiler they are not. You need to introduce a type variable that you can use in both places. – Matt Timmermans Apr 21 '21 at 12:37
  • @MattTimmermans I tried with `T` or `V` but I get the same compiler error. I edited the question to show that this is the case – lurscher Apr 21 '21 at 12:40

1 Answers1

1

As Matt Timmermans suggested, you need a type variable.

Changing allResultsOf to this compiles:

public static <T> CompletableFuture<List<T>> allResultsOf(List<CompletableFuture<T>> completableFutures) {
            CompletableFuture<Void> allFutures = CompletableFuture
                    .allOf(completableFutures.toArray(new CompletableFuture[completableFutures.size()]));

            CompletableFuture<List<T>> allCompletableFuture = allFutures.thenApply(
                    future -> {
                        System.out.println(String.format("%s <- future", future));
                        return completableFutures.stream()
                                .map(completableFuture -> completableFuture.join())
                                .collect(Collectors.toList());
                    });

            return allCompletableFuture;
        }
tgdavies
  • 10,307
  • 4
  • 35
  • 40