The Java type system supports only invariant types. So a List<String> is not an List<Object>. A List<String> is not a List<Object> as it is not valid to insert an Integer into a List<String>. However, there are types for which such a covariant type conversion is valid.
Given the classes A, B and Producer:
class A{}
class B{}
interface Producer<T> {
T next();
}
A cast for the covariant type Producer can be defined:
class Types{
@SuppressWarnings("unchecked")
public static <T> Producer<T> cast(Producer<? extends T> producer){
return (Producer<T>) producer;
}
}
This method supports to cast from Producer<A> to Producer<Object> and prevents invalid casts like Producer<A> to Producer<B>:
Producer<Object> valid = Types.<Object> cast(new Producer<A>());
Producer<A> invalid = Types.<A> cast(new Producer<B>()); //does not compile
My problem is that I cannot perform a cast from Producer<Producer<A>> to Producer<Producer<Object>>.
Producer<Producer<A>> producerOfA = new Producer<Producer<A>>();
Producer<Producer<Object>> producerOfObjects =
Types.<Producer<Object>> cast(producerOfA); //does not compile
Is there a way to persuade the Java type system to perform such a valid type conversion without warnings in user code?
You haven't posted the code for Producer, but based on the name and your assertion that it should be covariant, perhaps wherever you currently say:
Producer<Foo>
You should instead say:
Producer<? extends Foo>
It would be nice if Java would automatically realize that a generic interface was equivalent to its wildcarded forms (Iterator and Iterable are also safely covariant, for example), but for now at least, it doesn't.
Well, you could just go via raw types and do
Producer<Producer<String>> spp = ...;
Producer<Producer<Object>> opp = (Producer<Producer<Object>>)(Producer) spp;
But it's fugly and theoretically incorrect. You should use Producer<Producer<?>> (or Producer<? extends Producer<?>>), but if you really can't, I'd advise you to make a wrapper instead.
class CovariantProducer<T> implements Producer<T> {
private final Producer<? extends T> backing;
public CovariantProducer(Producer<? extends T> backing) {
this.backing = backing;
}
@Override
public T next(){ return backing.next(); }
}
// Usage:
Producer<String> sp = ...;
Producer<Object> op = new CovariantProducer<Object>(sp);
final Producer<Producer<String>> spp = ...;
Producer<Producer<Object>> opp = new Producer<Producer<Object>>() {
@Override
public Producer<Object> next() {
return new CovariantProducer<Object>(spp.next());
}
};
Somewhat more overhead, but this way you don't have to rape the type system, and the stuff that really doesn't work doesn't look like it works, either.
Edit: You could also do a special case of your cast method:
@SuppressWarnings("unchecked")
public static <T> Producer<Producer<T>> castMetaProducer(Producer<? extends Producer<? extends T>> producer){
return (Producer<Producer<T>>) producer;
}
However, if you're gonna want to turn a Producer<Producer<Producer<String>>> into a Producer<Producer<Producer<Object>>>, you'd have to add another method for that, and so on. Since this strictly speaking is incorrect usage of the type system, it's not very strange it's inconvenient to work this way.
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