Nutel
Nutel

Reputation: 2304

Generics method call with wildcard

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

Answers (4)

Marco13
Marco13

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 )

But...

... 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 Doubles), 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

squallsv
squallsv

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

biziclop
biziclop

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

mvieghofer
mvieghofer

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

Related Questions