Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

declaring double use of wildcards in java

I'm having trouble declaring a fully generic type for the output of a method. Here's the situation:

class A<S,T>{
public Callback<B<S,T>,C<S,T>> method();
}

in my code I have one of these;

A<String, ?> instance;

however, when I call

result = instance.method()

the only way to make the compiler happy is to declare

Callback<?, ?> result = instance.method()

But I would really like to be more specific with my generics and declare

Callback<B<String, ?>,C<String, ?>> result = instance.method()

however, since each wildcard is separate, the compiler complains that this is incorrect, stating that it cannot convert from

Callback<B<String,capture#3-of ?>,C<String,capture#3-of ?>>

Is there any way of declaring this situation correctly?

FYI, classes A, B, and C are from external libraries. I tried to declare my instance as

A<String, Object>

but it's also the result of a method from a fourth class that I have no control over:

class D<T>{
public A<T, ?> getObject();
}

D<String> start = new D<String>();
A<String, ?> = start.getObject();
...
like image 387
Jocken Avatar asked Jan 28 '26 11:01

Jocken


2 Answers

This is indeed an awkward situation, a result of the limitations of wildcard capture and a less than ideal return type from D.getObject.

There are a few workarounds but none are pretty. The first is to simply do an unchecked cast:

@SuppressWarnings("unchecked") //it's okay for the two captures not to match
Callback<B<String, ?>, C<String, ?>> result =
        (Callback<B<String, ?>, C<String, ?>>)instance.method();

EDIT: Some compilers will (more correctly) require a double cast through Callback<?, ?>:

@SuppressWarnings("unchecked") //it's okay for the two captures not to match
Callback<B<String, ?>, C<String, ?>> result =
        (Callback<B<String, ?>, C<String, ?>>)(Callback<?, ?>)instance.method();

You could also use a helper method - this is a common solution to wildcard capture issues:

static <T> void helper(A<String, T> a) {

    Callback<B<String, T>, C<String, T>> result = a.method();

    //logic
}

...

helper(instance);

UPDATE:

In fact you can combine these two approaches to at least isolate the ugliness into a single helper method:

static <S, T> Callback<B<S, ?>, C<S, ?>> disentangleCallback(
        Callback<B<S, T>, C<S, T>> callback
) {
    @SuppressWarnings("unchecked") //it's okay for the two captures not to match
    final Callback<B<S, ?>, C<S, ?>> withWidenedTypes =
            (Callback<B<S, ?>, C<S, ?>>)(Callback<?, ?>)callback;
    return withWidenedTypes;
}

And then use it like this:

Callback<B<String, ?>, C<String, ?>> result =
        disentangleCallback(instance.method());

Or else a similar method that simply takes an A<S, T> and returns a Callback<B<S, ?>, C<S, ?>> by calling method itself, then casting.


Another idea: if the second type argument of A is always an unknown, it might be easier to just remove it and widen the return type of method:

class A<S> {
    public Callback<B<S, ?>, C<S, ?>> method() { ... }
}

I realize that would be an unfortunate concession, but it makes sense if T is never actually useful.

like image 172
Paul Bellora Avatar answered Jan 30 '26 00:01

Paul Bellora


You could have

Callback<? extends B<String, ?>, ? extends C<String, ?>> result
    = instance.method();
like image 41
newacct Avatar answered Jan 30 '26 02:01

newacct



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!