guilhermebla...@gmail.com wrote on 29/04/2016 15:26:
You can add subtypes of A to a List<A> in Java. What List<? extends A>
>means is that the list itself may be a list of any type, provided that type
>is compatible with A. So if B extends A, List<B> is compatible with List<?
>extends A>, and when reading items you can assume they will be compatible
>with A (since B extends A) but you can't add an A (because it's actually a
>list of Bs).
>
>
Wrong. This is documented here
https://docs.oracle.com/javase/tutorial/java/generics/upperBounded.html  and
specifically states:

To write the method that works on lists of Number and the subtypes of Number,
>such as Integer, Double, and Float, you would specify List<? extends
>Number>. The term List<Number> is more restrictive than List<? extends
>Number> because the former matches a list of type Number only, whereas
>the latter matches a list of type Number or any of its subclasses.


Forgive me for butting in if I'm completely wrong, but is the confusion here between *declaring an instance* and *declaring a parameter constraint*?

I think the point is that you can declare variables as follows:

List<Integer> li;  // only accepts Integer
List<Double> ld; // only accepts Double
List<Number> ln; // accepts any Number, even if it is in fact an Integer or Double instance

But if you then define a function accepting a parameter:

public static double sumOfList(List<Number> list)


Now your argument has to be of type List<Number>; you can pass "ln" above, but not "li" or "ld". To relax the function's contract, you can write this instead:

public static double sumOfList(List<? extends Number> list)

Now the argument can be a List<Integer> or List<Double>, so "li" and "ld" are legal arguments.


The reason the default case is to be invariant, rather than covariant, is explained on the blog post Jesse linked to: http://hhvm.com/blog/9215/covariance-contravariance-and-super-type-constraints


In a nutshell, consider this function:

function addPiToList(List<Number> $list): void
{
$list->add(3.1415); // for the sake of example, assume 3.1415 instanceOf Double
}

If we allow covariance, then we can run addPiToList(new List<Integer>) and the type conditions are met; clearly this is not sane, and is going to result in an error somewhere.

So we need to be stricter:

function addPiToList(List<Double> $list): void

But the actual contract we want here is "any list we can legally add a Double to", and if we get a List<Number> or a List<*>, it would work fine, so our check is too strict. What's needed is a way to declare our function as contravariant:

function addPiToList(List<? super Double> $list): void

Now we can pass in a List<Double> or a List<Number>, but not a List<Integer>, and our contract holds nicely.


It took me a while to get my head around, but I think the above makes sense...

Regards,
--
Rowan Collins
[IMSoP]

Reply via email to