Java Generics WildCard Question: List? extends A
Method arguments are contravariant in the subtype, and by the definition of the wildcard, for every type T
that extends Vehicle
Foo<T>
is a subtype of Foo<* extends Vehicle>
. The implication of this is that wildcards are great when you only care about the return type, but dont work in situations like this when you want to pass a value of the type to a method.
The problem is that a user might try to call
List<SpaceShip> l = ...
addCars(l);
if your code were to compile, l
would then be a list of spaceships containing 3 cars. Clearly no good.
Java Generics WildCard: ? extends Number vs T extends Number
There is no difference in this case, because T
is never used again.
The reason for declaring a T
is so that you can refer to it again, thus binding two parameter types, or a return type together.
Java Generic Type for Wildcard extends allow to add only null
If you go through the Generic Guidelines stated by Oracle docs, these kind of list declarations are tend to be read-only (no write operations can be performed)
Quoted from the page
A list defined by
List<? extends ...>
can be informally thought of as
read-only, but that is not a strict guarantee.
As per your example, let us assume Child2
and Child3
extends Parent
, then
List<? extends Parent>
list can be a list of any of these subclasses. Adding a specific subclass element can cause runtime error, thus compiler complains in advance
Use of '? extends ' and '? super ' in Collection generics
The wildcards introduce restrictions in how the collection can be used.
For example, with List<? extends Number>
, I can't add new elements to the list. This is because all I know is that the list is some kind of subtype of Number
, but I don't know what that actual subtype is (so how could I know what to add?). For example, take the following code:
public void doSomethingWith(List<? extends Number> numbers) {
numbers.add(Integer.valueOf(0)); // Won't compile
}
This won't compile because both of these method calls are legal:
doSomethingWith(new ArrayList<Integer>());
doSomethingWith(new ArrayList<Double>());
What you can do is read elements from the list:
// This will all compile
public void doSomethingWith(List<? extends Number> numbers) {
for (Number number : numbers) {
// Do something with number
}
// OR
Number number = numbers.get(0);
// OR
Number number = numbers.remove(0);
}
Calls to methods like get
will return some kind of Number
, we know that for a fact because of the ? extends Number
, so we can treat it like that for reading purposes.
On the other hand, List<? super Integer>
has exactly the opposite result. I can no longer read from the list, but I can write to it. I know that whatever ?
is, it will definitely be a super-class of Integer
, so concrete types of the list will definitely accept Integer
values. For example:
public void doSomethingWith(List<? super Integer> integers) {
integers.add(Integer.valueOf(0));
}
That code is completely legal. However, if you want to read from the list, the only way to do this is to use Object
since anything else requires casting (which requires knowing its concrete type):
for (Object obj : integers)
// OR
Object obj = integers.get(0);
// OR
Object obj = integers.remove(0);
What's Really Happening
Here's what's actually happening. When you specify ? extends Number
, you're making any method that takes elements as a parameter unusable. In fact, if you try to auto-complete code in Eclipse using Ctrl+Space on a List<? extends Number>
, it shows null
as the parameters' types in the add
methods and the like. Meanwhile, all the methods that return elements are guaranteed to return at least some kind of Number
, though you won't know exactly which subclass of Number
it might actually be.
When you specify ? super Integer
, you're making any method that takes elements as a parameter guarantee that they'll accept Integer
values (and sub-classes of Integer
as well). This allows you to call methods like add
since you know they'll accept Integer
types. Meanwhile, all methods that return elements are only guaranteed to return something, but we don't know what, so all the methods that return elements are only guaranteed to return Object
.
PECS is an excellent acronym to remember this, it means "Producer Extends, Consumer Supers". This means that if you want your list to give you something, it's a producer, and you should use extends
. If you want your list to accept things from you, it's a consumer, so you use super
. See this answer for more.
But what if I have a wildcard with no bounds?
It does both! <?>
restricts you from calling methods that take the generic type as an argument and causes all the methods that return the generic type to return Object
. This is because we have no idea what the type is whatsoever. For example, all of these assignments into a List<?>
are legal:
List<?> list;
list = new ArrayList<Integer>();
list = new ArrayList<String>();
list = new ArrayList<MyClass>();
And so on.
When to use wildcards in Java Generics?
The big difference between
public <T extends Animal> void takeThing(ArrayList<T> list)
and
public void takeThing(ArrayList<? extends Animal> list)
is that in the former method you can refer to "T" within the method as the concrete class that was given. In the second method you cannot do this.
Here a more complex example to illustrate this:
// here i can return the concrete type that was passed in
public <T extends Animal> Map<T, String> getNamesMap(ArrayList<T> list) {
Map<T, String> names = new HashMap<T, String>();
for (T animal : list) {
names.put(animal, animal.getName()); // I assume there is a getName() method
}
return names;
}
// here i have to use general Animal
public Map<Animal, String> getNamesMap(ArrayList<? extends Animal> list) {
Map<Animal, String> names = new HashMap<Animal, String>();
for (Animal animal : list) {
names.put(animal, animal.getName()); // I assume there is a getName() method
}
return names;
}
With the first method if you pass in an List of Cats you get a Map with Cat as key. The second method would always return a Map with general Animal key.
By the way this is not valid java syntax:
public <? extends Animal> void takeThing(ArrayList<?> list)
Using this form of generic method declaration you have to use a valid java identifier and not "?".
Edit:
The form "? extends Type" only applies to variable or parameter type declaration. Within a generic method declration it has to be "Identifier extends Type" as you are able to refer to the "Identifier" from within your method.
Can't add value to the Java collection with wildcard generic type
It's doing that for the sake of safety. Imagine if it worked:
List<Child> childList = new ArrayList<Child>();
childList.add(new Child());
List<? extends Parent> parentList = childList;
parentList.set(0, new Parent());
Child child = childList.get(0); // No! It's not a child! Type safety is broken...
The meaning of List<? extends Parent>
is "The is a list of some type which extends Parent
. We don't know which type - it could be a List<Parent>
, a List<Child>
, or a List<GrandChild>
." That makes it safe to fetch any items out of the List<T>
API and convert from T
to Parent
, but it's not safe to call in to the List<T>
API converting from Parent
to T
... because that conversion may be invalid.
Related Topics
Why Does This Generic Code Compile in Java 8
Java - Convert Image to Base64
Jdbc Connection to Mssql Server in Windows Authentication Mode
JPA Onetomany Not Deleting Child
Making Distinctions Between Different Kinds of Jsf Managed-Beans
Java Lib or App to Convert CSV to Xml File
Uses for the Java Void Reference Type
How to Change the Arrow Style in a Jcombobox
Sorting Strings That Contains Number in Java
Pageloadtimeout in Selenium Not Working
What Are the Best Use Cases for Akka Framework
What Is the Most Efficient Java Collections Library
Why People Are So Afraid of Using Clone() (On Collection and Jdk Classes)
How to Use Mockito When We Cannot Pass a Mock Object to an Instance of a Class