Java generics - ArrayList initialization

后端 未结 6 1629
南方客
南方客 2020-12-02 13:58

It is known that arraylist init. should be like this

ArrayList a = new ArrayList();
ArrayList a = new ArrayList

        
相关标签:
6条回答
  • 2020-12-02 14:18

    Think of the ? as to mean "unknown". Thus, "ArrayList<? extends Object>" is to say "an unknown type that (or as long as it)extends Object". Therefore, needful to say, arrayList.add(3) would be putting something you know, into an unknown. I.e 'Forgetting'.

    0 讨论(0)
  • 2020-12-02 14:20

    The key lies in the differences between references and instances and what the reference can promise and what the instance can really do.

    ArrayList<A> a = new ArrayList<A>();
    

    Here a is a reference to an instance of a specific type - exactly an array list of As. More explicitly, a is a reference to an array list that will accept As and will produce As. new ArrayList<A>() is an instance of an array list of As, that is, an array list that will accept As and will produce As.

    ArrayList<Integer> a = new ArrayList<Number>(); 
    

    Here, a is a reference to exactly an array list of Integers, i.e. exactly an array list that can accept Integers and will produce Integers. It cannot point to an array list of Numbers. That array list of Numbers can not meet all the promises of ArrayList<Integer> a (i.e. an array list of Numbers may produce objects that are not Integers, even though its empty right then).

    ArrayList<Number> a = new ArrayList<Integer>(); 
    

    Here, declaration of a says that a will refer to exactly an array list of Numbers, that is, exactly an array list that will accept Numbers and will produce Numbers. It cannot point to an array list of Integers, because the type declaration of a says that a can accept any Number, but that array list of Integers cannot accept just any Number, it can only accept Integers.

    ArrayList<? extends Object> a= new ArrayList<Object>();
    

    Here a is a (generic) reference to a family of types rather than a reference to a specific type. It can point to any list that is member of that family. However, the trade-off for this nice flexible reference is that they cannot promise all of the functionality that it could if it were a type-specific reference (e.g. non-generic). In this case, a is a reference to an array list that will produce Objects. But, unlike a type-specific list reference, this a reference cannot accept any Object. (i.e. not every member of the family of types that a can point to can accept any Object, e.g. an array list of Integers can only accept Integers.)

    ArrayList<? super Integer> a = new ArrayList<Number>();
    

    Again, a is a reference to a family of types (rather than a single specific type). Since the wildcard uses super, this list reference can accept Integers, but it cannot produce Integers. Said another way, we know that any and every member of the family of types that a can point to can accept an Integer. However, not every member of that family can produce Integers.

    PECS - Producer extends, Consumer super - This mnemonic helps you remember that using extends means the generic type can produce the specific type (but cannot accept it). Using super means the generic type can consume (accept) the specific type (but cannot produce it).

    ArrayList<ArrayList<?>> a
    

    An array list that holds references to any list that is a member of a family of array lists types.

    = new ArrayList<ArrayList<?>>(); // correct
    

    An instance of an array list that holds references to any list that is a member of a family of array lists types.

    ArrayList<?> a
    

    An reference to any array list (a member of the family of array list types).

    = new ArrayList<?>()
    

    ArrayList<?> refers to any type from a family of array list types, but you can only instantiate a specific type.


    See also How can I add to List<? extends Number> data structures?

    0 讨论(0)
  • 2020-12-02 14:22

    You have strange expectations. If you gave the chain of arguments that led you to them, we might spot the flaw in them. As it is, I can only give a short primer on generics, hoping to touch on the points you might have misunderstood.

    ArrayList<? extends Object> is an ArrayList whose type parameter is known to be Object or a subtype thereof. (Yes, extends in type bounds has a meaning other than direct subclass). Since only reference types can be type parameters, this is actually equivalent to ArrayList<?>.

    That is, you can put an ArrayList<String> into a variable declared with ArrayList<?>. That's why a1.add(3) is a compile time error. a1's declared type permits a1 to be an ArrayList<String>, to which no Integer can be added.

    Clearly, an ArrayList<?> is not very useful, as you can only insert null into it. That might be why the Java Spec forbids it:

    It is a compile-time error if any of the type arguments used in a class instance creation expression are wildcard type arguments

    ArrayList<ArrayList<?>> in contrast is a functional data type. You can add all kinds of ArrayLists into it, and retrieve them. And since ArrayList<?> only contains but is not a wildcard type, the above rule does not apply.

    0 讨论(0)
  • 2020-12-02 14:22

    A lot of this has to do with polymorphism. When you assign

    X = new Y();
    

    X can be much less 'specific' than Y, but not the other way around. X is just the handle you are accessing Y with, Y is the real instantiated thing,

    You get an error here because Integer is a Number, but Number is not an Integer.

    ArrayList<Integer> a = new ArrayList<Number>(); // compile-time error
    

    As such, any method of X that you call must be valid for Y. Since X is more generally it probably shares some, but not all of Y's methods. Still, any arguments given must be valid for Y.

    In your examples with add, an int (small i) is not a valid Object or Integer.

    ArrayList<?> a = new ArrayList<?>();
    

    This is no good because you can't actually instantiate an array list containing ?'s. You can declare one as such, and then damn near anything can follow in new ArrayList<Whatever>();

    0 讨论(0)
  • 2020-12-02 14:33

    You can't assign a List<Number> to a reference of type List<Integer> because List<Number> allows types of numbers other than Integer. If you were allowed to do that, the following would be allowed:

    List<Number> numbers = new ArrayList<Number>();
    numbers.add(1.1); // add a double
    List<Integer> ints = numbers;
    Integer fail = ints.get(0); // ClassCastException!
    

    The type List<Integer> is making a guarantee that anything it contains will be an Integer. That's why you're allowed to get an Integer out of it without casting. As you can see, if the compiler allowed a List of another type such as Number to be assigned to a List<Integer> that guarantee would be broken.

    Assigning a List<Integer> to a reference of a type such as List<?> or List<? extends Number> is legal because the ? means "some unknown subtype of the given type" (where the type is Object in the case of just ? and Number in the case of ? extends Number).

    Since ? indicates that you do not know what specific type of object the List will accept, it isn't legal to add anything but null to it. You are, however, allowed to retrieve any object from it, which is the purpose of using a ? extends X bounded wildcard type. Note that the opposite is true for a ? super X bounded wildcard type... a List<? super Integer> is "a list of some unknown type that is at least a supertype of Integer". While you don't know exactly what type of List it is (could be List<Integer>, List<Number>, List<Object>) you do know for sure that whatever it is, an Integer can be added to it.

    Finally, new ArrayList<?>() isn't legal because when you're creating an instance of a paramterized class like ArrayList, you have to give a specific type parameter. You could really use whatever in your example (Object, Foo, it doesn't matter) since you'll never be able to add anything but null to it since you're assigning it directly to an ArrayList<?> reference.

    0 讨论(0)
  • 2020-12-02 14:34
    ArrayList<Integer> a = new ArrayList<Number>(); 
    

    Does not work because the fact that Number is a super class of Integer does not mean that List<Number> is a super class of List<Integer>. Generics are removed during compilation and do not exist on runtime, so parent-child relationship of collections cannot be be implemented: the information about element type is simply removed.

    ArrayList<? extends Object> a1 = new ArrayList<Object>();
    a1.add(3);
    

    I cannot explain why it does not work. It is really strange but it is a fact. Really syntax <? extends Object> is mostly used for return values of methods. Even in this example Object o = a1.get(0) is valid.

    ArrayList<?> a = new ArrayList<?>()
    

    This does not work because you cannot instantiate list of unknown type...

    0 讨论(0)
提交回复
热议问题