I took a look on questions q1, q2, q3, but they don't cover exactly my question.
Note that ArrayList<A> and ArrayList<? extends A>
are to be used for declaring a variable or a parameter (not for creating a new generic class).
Are both expressions equivalent when declaring an object attribute (case 1)?:
class Foo {
private ArrayList<A> aList; // == ArrayList<? extends A> aList;
}
EDIT: Are both expressions equivalent from the point of view of what
kind of objects are allowed to be added to aList
?, but different
in the same sense as the following case?
but they are different when used in a parameter declaration (case 2)?:
void methodFoo(ArrayList<A> al) != void methodFoo(ArrayList<? extends A> al)
because the first one only allows to be passed ArrayList objects
while the second would be like "more permissive" allowing to be sent
ArrayList<A1>
and ArrayList<A2>
(as long as A1 and A2 extends A)?
If this is right, is there any other scenario where the two expressions are effectively different?
Thanks,
Let's have a look at some practical examples. Say, you have:
List<Number> list;
This means that whatever is assigned to this variable or field takes Number
and outputs Number
, so you always know what to expect. Integer
can be added to this list since Integer
extends Number
. However, you can't assign, say, ArrayList<Long>
to this list.
But consider this case:
List<? extends Number> list;
This one says: hey, that's a list of something that extends Number
, but no one knows what exacty. What does this mean? This means that you can assign, for example, ArrayList<Long>
to this list, which you couldn't in the first case. You still know that whatever this list outputs will be a Number
, but you can't put an Integer
in it anymore.
There is also an opposite case:
List<? super Number> list;
By printing that you say: that's a list of Number
or its superclasses. This is where everything becomes vice-versa. The list can now refer to ArrayList<Object>
and ArrayList<Number>
. Now we don't know what this list will output. Will it be a Number
? Will it be an Object
? But now we know that we could put a Number
in this list as well as any subclass of Number
like Integer
or Long
.
There is a rule, by the way, which says producer extends, consumer super (PECS for short). If you need the list to output the values, it is a producer, this is the second case. If you need the list to accept values, it is a consumer, this is the third case. If you need both, don't use wildcards (that's the first case).
I hope this clears up matters.
This will explain the difference:
public class GenericsTest {
private ArrayList<A> la;
private ArrayList<? extends A> lexta;
void doListA(ArrayList<A> la) {}
void doListExtA(ArrayList<? extends A> lexta) {}
void tester() {
la = new ArrayList<SubA>(); // Compiler error: Type mismatch
doListA(new ArrayList<SubA>()); // Compiler error: Type mismatch
lexta = new ArrayList<SubA>();
doListExtA(new ArrayList<SubA>());
}
static class A {}
static class SubA extends A {}
}
As you see, calling a method and assigning a variable/instance field have the same rules. Look at the method call as an assignment of your argument to its declared parameter.
ArrayList<A>
means a specific class A, where as ArrayList<? extends A>
means class A or any class which extands A (Sub class of A) this make it more generic
Using private ArrayList<A> aList;
as a variable declaration is not really equivalent to using the wildcard private ArrayList<? extends A> aList;
The wildcarded version will allow you to assign any ArrayLists of types that extend A and A itself but will refuse to add elements to the list as it cannot decide if it is type safe. With ArrayList<A>
on the other hand you can only assign ArrayLists (or extensions of ArrayList) of type A and you can then add A elements and any elements extending A to it.
FYI: you should prefer using a more abstract type for declaring your variables/parameters like List<A>
or Collection<A>
.
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