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