Java Covariant Type Conversion Support

The Java type system only supports invariant types. So it is List<String>

not List<Object>

. A is List<String>

not List<Object>

because it is wrong to insert Integer

into List<String>

. However, there are types for which such a covariant type conversion is valid.

For classes A, B and Producer:

class A{}
class B{}
interface Producer<T> {
    T next();
}

      

Cast for 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 dropping from Producer<A>

to Producer<Object>

and prevents invalid broadcasts such as Producer<A>

- 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 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 convince the Java type system to perform such a valid type conversion without warning in user code?

+2


source to share


5 answers


You haven't posted the code for Producer, but based on the name and your statement that it must be covariant, perhaps wherever you say:

Producer<Foo>

      

Instead, you should say:



Producer<? extends Foo>

      

It would be nice if Java would automatically figure out that the generic interface is equivalent to its wildcard forms ( Iterator

and is Iterable

also safely covariant, for example), but for now, at least that's not the case.

+7


source


Well, you can just go through the raw types and do

Producer<Producer<String>> spp = ...;
Producer<Producer<Object>> opp = (Producer<Producer<Object>>)(Producer) spp;

      

But he is wrong and theoretically wrong. You should use Producer<Producer<?>>

(or Producer<? extends Producer<?>>

), but if you really can't, I would suggest you make a shell.

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());
    }
};

      

A bit more overhead, but this way you don't have to rape the type system and stuff that doesn't really work doesn't seem to work.




Edit: You can also make a special case of your method cast

:

@SuppressWarnings("unchecked")
public static <T> Producer<Producer<T>> castMetaProducer(Producer<? extends Producer<? extends T>> producer){
    return (Producer<Producer<T>>) producer;
}

      

However, if you want to convert Producer<Producer<Producer<String>>>

to Producer<Producer<Producer<Object>>>

, you will have to add another method for this, etc. Since this is, strictly speaking, an incorrect use of the type system, it is not very strange that it is inconvenient to work this way.

+2


source


Java provides no way to specify that a generic type should be covariant (or contravariant). In your case, if I can cast from Manufacturer <A -> to Manufacturer <A -> then I can do this:

Producer<Producer<Object>> objProducerProducer = cast(Producer<Producer<A>>);
Producer<Object> objProducer = objProducerProducer.produce()

      

But of course objProducerProducer actually produces Producer <A> objects. Java cannot pass them to Producer <Object> automatically, this is exactly how it happens. Your explicit static static method works because you require <? extends T>. Generics do not apply to each other. I assume what you want is implicit conversions that are integrated with the generics system, which is a long jump from what we have.

Edit: To clarify, yes, in your case, it is safe to treat Producer <A> as Producer <Object>, but this is knowledge that is not available to the generics system.

+1


source


What you have embedded in yourself. A typeclass is not a cast, it is just a convenient way to hide compiler warnings (and, I think, a convenient way to trick colleagues). So instead of "casting" your producer, I would rather change the Producer implementation to something like this (if possible):

class Producer {
  <T> T produce(Class<T> cls) {
    Object o;

    // do something, e.g.
    // o = cls.newInstance();

    // perfectly type safe cast
    return cls.cast(o);
  }
}

Producer p = new Producer();
A a = p.produce(A.class);

      

Note. It may not help you with your specific problem, but it may point you in the right direction.

+1


source


This is what needs to be handled with wildcards on the usage site in Java.

However, you can do this explicitly with a proxy in Java.

public static <T> Iterator<T> clean(final Iterator<? extends T> orig) {
    return new Iterator<T>() {
        public boolean hasNext() {
             return orig.hasNext();
        }
        public T next() {
             return orig.next();
        }
        public void remove() {
             return orig.remove();
        }
    };
}

      

+1


source







All Articles