2

I have recently taken on a Java course about generics and wildcards. I am not able to grasp the difference between them and, sometimes, even the need to use them.

Two questions, please:

1) An order has a list of products, and we have sheets and clips as products.

public class Product {}

public class Sheet extends Product{}

public class Clip extends Product{}

public class Order {
    //isn't this:
    private List<Product> products;

    //same as this:
    private List<? extends Product> products;
}

2.1) When using the first (List<Product>):

public Order() {//Order constructor, trying to create an order of sheets

    this.products = new ArrayList<Sheet>();//compiler asks for cast
}

if I try to compile without casting anyway, error says:

Uncompilable source code - incompatible types: java.util.ArrayList< Sheet > cannot be converted to java.util.List< Product >

then event if I do this:

public Order(){
     products = new ArrayList<>();
     products.add(new Sheet()); //again asks for a cast
}

try to compile anyways, error says:

Uncompilable source code - Erroneous sym type: java.util.List.add

2.2) When using the second (List<? extends Product>):

public Order() {//Order constructor, trying to create an order of sheets

    this.products = new ArrayList<Sheet>();//compiler asks for cast as well
}

if I try to compile without casting anyway, error says:

incompatible types: java.util.ArrayList< Sheet > cannot be converted to java.util.List< ? extends Product >

then event if I do this:

public Order(){
     products = new ArrayList<>();
     products.add(new Sheet()); //no suitable method found for add(Sheet), 
                                //method Collection.add(CAP#1) is not applicable
}

try to compile anyways, error says:

Uncompilable source code - Erroneous sym type: java.util.List.add

Scaramouche
  • 3,188
  • 2
  • 20
  • 46

2 Answers2

1

An ArrayList<Sheet> is not a subtype of an ArrayList<Product>.

You can't use it everywhere an ArrayList<Product> is expected, especially into an ArrayList<Product> you can legally add a Clip, but you shouldn't do that with an ArrayList<Sheet>.

Ralf Kleberhoff
  • 6,990
  • 1
  • 13
  • 7
0

The type of the variable declaration should match the type you pass in actual object type. So:

List<Product> list = new ArrayList<Clip>();

OR

 List<Product> list = new ArrayList<Sheet>(); 

will not compile.

The reason behind this is that you might end up putting a Clip object in a list that was supposed to contain only Sheets and eventually end up treating it like a Sheet.

Developers seem to get confused because it is allowed in case of Arrays.

Product[] array = new Sheet[10]; // Compiles fine

It's allowed in case of arrays because there's a runtime exception(ArrayStoreException) that prevents you putting a wrong type of object in the array. (Consider you do this for the above array: array[0] = new Clip();)

There's not equivalent exception in case of lists because JVM knows the type of Array at run time but not of List because of type erasure.

Also, private List<Product> products; is not equivalent to private List<? extends Product> products in the sense that you can add any product or subtype of product in private List<Product> products; but you can't add anything (except null) to private List<? extends Product> products. This syntax is mostly used when you want to pass your list to a method that only reads the data from the list. Like below:

private void readProduct(List<? extends Product> list){
//read product list
}

This method will accept List<Clip> and List<Sheet> both. But adding anything to it will result in compilation error (except null).

Aditya Narayan Dixit
  • 2,105
  • 11
  • 23