I need to create a generic array of CompletableFuture
objects so that I can pass it to the CompletableFuture.allOf
method to get one CompletableFuture
to sync the threads. But since it is a generic I cannot create it. One obvious solution is to create a List and then call toArray
on it but it'll be inefficient. Are there any better methods? Here is my code:
// Current solution:
List<CompletableFuture<List<ReportComparable>>> newReports = new ArrayList<>();
// Loop and add CompletableFuture objects to this list
// Collect all the retrieved objects here(Sync Threads).
try {
List<List<ReportComparable>> newReps = CompletableFuture.allOf((CompletableFuture<?>[]) newReports.toArray()).get();
} catch (InterruptedException | ExecutionException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
Java allows generic classes, methods, etc. that can be declared independent of types. However, Java does not allow the array to be generic. The reason for this is that in Java, arrays contain information related to their components and this information is used to allocate memory at runtime.
You can do this: E[] arr = (E[])new Object[INITIAL_ARRAY_LENGTH]; This is one of the suggested ways of implementing a generic collection in Effective Java; Item 26. No type errors, no need to cast the array repeatedly.
If generic array creation were legal, then compiler generated casts would correct the program at compile time but it can fail at runtime, which violates the core fundamental system of generic types.
There are several wrong assumptions in your question
You can’t do (CompletableFuture<?>[]) newReports.toArray()
. The parameterless toArray()
method will return Object[]
and the casting attempt will cause a ClassCastException
. Only the toArray(T[])
method accepting an existing array will return an array of the same type, which brings you back to square zero
CompletableFuture.allOf
returns a CompletableFuture<Void>
, so you can’t call get()
on it and expect to get a List<List<ReportComparable>>
. You have to assemble the result list yourself after completion.
There is no problem creating an array of a generic class, when all type arguments are wildcards. So
CompletableFuture<?>[] array = new CompletableFuture<?>[100];
works.
When you have an arbitrary number of elements, adding them to a List
and invoking toArray
on it, isn’t necessarily inefficient. The alternative, dealing with an array and an index manually, is doing the same as an ArrayList
, but error prone. The single copying step of toArray
is rarely performance relevant.
Also keep in mind, since the CompletableFuture.allOf
returns CompletableFuture<Void>
you might need the List<CompletableFuture<List<ReportComparable>>>
anyway to be able to construct the desired List<List<ReportComparable>>
after the completion.
On the other hand, when you have a fixed number of arguments, you may call the varargs method CompletableFuture.allOf
directly without manual array creation.
But when all you want to do with the CompletableFuture
returned by allOf
, is to call get()
immediately, the “wait for all” operation doesn’t have any benefit anyway.
You get the same effect implicitly when querying the individual CompletableFuture
instances via get()
or join()
and adding the result to your resulting List
, which you have to do anyway after completion, as CompletableFuture.allOf
does not do that for you.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With