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
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).
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>.
来源:https://stackoverflow.com/questions/53796147/generics-and-wildcards-in-java