Generics and wildcards in Java [duplicate]

谁说我不能喝 提交于 2019-12-07 23:26:01

问题


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


回答1:


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




回答2:


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

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!