Wednesday, April 29, 2009

Java generics mystery

Here is a puzzle for Generics gurus.

Can somebody explain why

Set<Address> addresses = new HashSet<Address>();
Set<?> things = addresses;

compiles but

Set<ConstraintValidator<Address>> validatedAddresses = 
    new HashSet<ConstraintValidator<Address>>();
Set<ConstraintValidator<?>> validatedThings =
    validatedAddresses;

does not compile?

More specifically, the assignment on the second line breaks.

3 comments:

Vivien Barousse said...

That's because ConstraintValidator<?> is not equals to ConstraintValidator<Address>. Parametrized types are expected to be strictly equals.

You can solve this issue by using generic wildcards :
Set<? extends ConstraintValidator<?>> validatedThings = validatedAddresses;

This is discussed more in the Java language specification, §4.5.

Unknown said...

Yes but the surprising thing is that I can do
ConstraintValidator<Address> a;
ConstraintValidator<?> thing = a;

But as soon as I use a Set<ConstraintValidator<Address>> which is a container of ConstraintValidator<Address>, I cannot assign it to Set<ConstraintValidator<?>>

Set<? extends ConstraintValidator<?>> works but it seems to imply that ? extends ConstraintValidator<?> contains ConstraintValidator<Address> while a plain ConstraintValidator<?> does not (which contradicts my first statement).

Josh McDonald said...

Set<SubClass> extends Set<?>, but it doesn't extend Set<BaseClass>. Annoying, but the way of things.

Replace SubClass with Set<OtherClass>, and BaseClass with Set<?>. Even though Set<OtherClass> extends Set<?>, Set<Set<OtherClass>> will still not extend Set<Set<?>>. Given the first situation, I think it would be strange to expect otherwise.