简体   繁体   中英

Understanding wildcards in Java generics

I'm not sure why the last statement in the following code is illegal. Integer should be a subtype of ? , so why can't I assign it to b ?

List<String> a = new ArrayList<String>();
a.add("foo");

// b is a List of anything
List<?> b = a;

// retrieve the first element
Object c = b.get(0);
// This is legal, because we can guarantee
// that the return type "?" is a subtype of Object

// Add an Integer to b.
b.add(new Integer (1)); 

The point is that b refers to a list of some type, but the compiler doesn't know what the type is, so it doesn't know whether or not it's valid to add an Integer to it. And a good thing too, given your example - you'd be adding an Integer to an object initially created to hold a list of strings. Sure, that information is lost at execution time in Java - but the compiler tries to keep you as safe as it can.

See the Java generics FAQ for a lot more information.

Integer is not a subtype of ? (necessarily). ? is a wildcard; you should interpret it as meaning "unknown".

So a List<?> is not the same as a List<Object> . You can add anything you like to a List<Object> .

The reference "b" is declared as a List, that is, a "List of something I don't know yet". You can assign pretty much any implementation to this reference, like a List. This is why it is forbidden to add anything to the lists through this reference.

Here is a short summary of what you can and cannot do with generics:

    List<? extends Number> listOfAnyNumbers = null;
    List<Number> listOfNumbers = null;
    List<Integer> listOfIntegers = null;
    listOfIntegers = listOfNumbers;     // Error - because listOfNumbers may contain non-integers
    listOfNumbers = listOfIntegers;     // Error - because to a listOfNumbers you can add any Number, while to listOfIntegers you cannot.
    listOfIntegers = listOfAnyNumbers;  // Error - because listOfAnyNumbers may contain non-integers  
    listOfAnyNumbers = listOfIntegers;  // OK    - because listOfIntegers is a list of ?, where ? extends Number.
    listOfNumbers = listOfAnyNumbers;   // Error - because listOfAnyNumbers may actually be List<Float>, to which you cannot add any Number. 
    listOfAnyNumbers = listOfNumbers;   // OK    - because listOfNumbers is a list of ?, where ? extends Number.

It's because we cannot guarantee that Integer is a subtype of the parameter type "?".

Look this:

Object c = b.get(0);

This is valid, as ? will always be an subtype from Object.

A rough rule of thumb for collections and generics is the following:

  • Collection<Foo> is a Collection from which you can get a Foo and to which you can add a Foo.
  • Collection<? extends Foo> Collection<? extends Foo> is a Collection from which you can get a Foo, but you cannot add anything .

Why is this so? Because when you say Collection<Foo> , you're promising to the users of that reference that they can invoke an add(Foo elem) method on the object in question. On the other hand, when you use the wildcard version, you're keeping the "real" parameter class a secret from the users of the reference—they know that any element they extract from the collection can be cast to Foo, but not whether they can add any Foo to it.

Why is this useful? Because there are many, many, many cases where you will write methods that will want to iterate through a Collection whose elements are all Foos, but to which you never need to add any elements. So like this:

public Foo findAFooThatILike(Collection<? extends Foo> foos);

Using the wildcard here means that the method will accept as its argument a Collection<Foo> and a collection of any subtype of Foo; eg, if Bar is a subtype of Foo, the signature above means that you can pass a Collection<Bar> to the method.

If on the other hand, you'd written the signature like this:

public Foo findAFooThatILike(Collection<Foo> foos);

...then you would not be able to pass in a Collection<Bar> as an argument. Why? Because for something to be a Collection<Foo> , it needs to support an add(Foo elem) method, and a Collection<Bar> doesn't.

Note that these rules of thumb only apply to Collection interfaces and classes. (Also note that Collection<? extends Foo> doesn't mean "read-only Collection of Foo"; many methods to remove elements from a collection can still work when you don't know the precise element type).

So, back to your original question: List<?> is the same as List<? extends Object> List<? extends Object> . It's a list from which you can get references to Object instances, but you cannot safely add anything.

List<?> means a list typed to an unknown type.This could be a List of Integer, String or XYZ class.

Since you don't know what type of list is typed to, you can only read from the collection, and you can only treat the Objects read as being Object instance.

Please go for super wildcard boundary if you want to insert elements in the wildcard generic collection.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM