Reputation: 2304
I have a method submit
:
<T> Future<Optional<T>> submit(Value<T> value) {
//...
}
and a call of this method.
Value<?> value = null;
Future<Optional<?>> future = submit(value);
But it does not compile unless I change the signature to Future<? extends Optional<?>> future
because submit
returns Future<Optional<caputure of ?>>
which is not Future<Optional<?>>
since generics are invariant. Is there any way to give a hint to a compiler and call the method submit
so it returns Future<Optional<?>>
?
This snippet
<T, E extends Future<Optional<T>>> E submit(Value<T> value) {
// ...
}
Value<?> value = null;
Future<Optional<?>> future = submit(value);
compiles as well but then I need to do unsafe cast to E before returning the value.
Here is an answer explaining the generics invariance but it still does not answer how to work with captured types.
Multiple wildcards on a generic methods makes Java compiler (and me!) very confused
Upvotes: 4
Views: 409
Reputation: 54709
Although I'm not sure why using it as Future<? extends Optional<?>>
should be a problem, there may be a solution, depending on how many contortions and "tricks" you are willing to accept.
It is not possible to compile it in the desired form, even when using the additonal type parameter E
, simply because it is not type safe. At least, it is not possible (for the compiler) to make sure that it is type safe. The reason why it could not be type safe can be summarized as follows: Someone receiving the Future<Optional<?>>
could modify the Future
, and assign it any Optional
- even one that has a different type than the one that it was originally created with. At a different location, someone could know the Future
with its original type, and receive a ClassCastException
when trying to use it. (I explained this with a simpler example in https://stackoverflow.com/a/22193221/3182664 )
... all this is not relevant here. The Future
interface does not allow to "set" any new value. Thus, it is perfectly feasible to convert it into a Future
with a supertype of its original type.
Note: This is similar to the fact that you can write
List<Integer> integers = new ArrayList<Integer>();
List<Number> numbers = Collections.unmodifiableList(integers);
This is valid (meaning "type safe") because you can not pollute the Integer
-list with invalid Number
instances (like Double
s), because it's not possible to modify the list anyhow!
So one type-safe, warning-free and valid solution could be to introduce a method that "wraps" a Future<? extends T>
and returns it as a Future<T>
. Again: This is not really pretty, and may not be worth the effort, but at least one option:
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
public class GenericOptionalFutureMethod
{
void call()
{
Value<?> value = null;
Future<Optional<?>> future =
FutureWrapper.<Optional<?>>wrap(submit(value));
}
<T> Future<Optional<T>> submit(Value<T> value) {
return null;
}
}
class FutureWrapper
{
static <T> Future<T> wrap(final Future<? extends T> future)
{
return new Future<T>()
{
@Override
public boolean cancel(boolean mayInterruptIfRunning)
{
return future.cancel(mayInterruptIfRunning);
}
@Override
public boolean isCancelled()
{
return future.isCancelled();
}
@Override
public boolean isDone()
{
return future.isDone();
}
@Override
public T get() throws InterruptedException, ExecutionException
{
return future.get();
}
@Override
public T get(long timeout, TimeUnit unit)
throws InterruptedException, ExecutionException,
TimeoutException
{
return future.get();
}
};
}
}
Upvotes: 2
Reputation: 492
You can change your method to use a wildcard operator as a parameter, since you are not specifying any type on T
. Seems to me, that it wouldn't change much.
Future<Optional<?>> submit(Value<?> value) {
//...
}
Upvotes: 0
Reputation: 49804
I don't see where the problem is really.
Future<? extends Optional<?>> future = submit(value);
Optional<?> f = future.get();
Compiles and runs correctly.
The answer you linked to explains it quite clearly:
Foo<Bar>
is a Foo<?>
But
Foo<Bar<X>>
isn't a Foo<Bar<?>>
, it's a Foo<? extends Bar<?>>
Upvotes: 0
Reputation: 2896
As far as I know there is no way of telling the compiler that your code is actually correct. You can try replacing the ?
with Object
:
Value<Object> value = null;
Future<Optional<Object>> future = submit(value);
Upvotes: 1