Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What is the practical difference between "ArrayList<A>" and "ArrayList<? extends A>"?

Tags:

java

generics

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,

like image 452
cibercitizen1 Avatar asked Apr 14 '12 10:04

cibercitizen1


4 Answers

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.

like image 132
Malcolm Avatar answered Nov 11 '22 20:11

Malcolm


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.

like image 26
Marko Topolnik Avatar answered Nov 11 '22 21:11

Marko Topolnik


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

like image 30
Adil Avatar answered Nov 11 '22 22:11

Adil


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>.

like image 33
nansen Avatar answered Nov 11 '22 20:11

nansen