Please explain this generic code wildcard compile time error:
//no compile time error.
List<? extends Number> x = new ArrayList<>();
//compile time error.
List<? extends Number> x = new ArrayList<? extends Number>();
Guidelines for Wildcards. Upper bound wildcard − If a variable is of in category, use extends keyword with wildcard. Lower bound wildcard − If a variable is of out category, use super keyword with wildcard. Unbounded wildcard − If a variable can be accessed using Object class method then use an unbound wildcard.
In generic code, the question mark (?), called the wildcard, represents an unknown type. The wildcard can be used in a variety of situations: as the type of a parameter, field, or local variable; sometimes as a return type (though it is better programming practice to be more specific).
We can use the Java Wildcard as a local variable, parameter, field or as a return type. But, when the generic class is instantiated or when a generic method is called, we can't use wildcards. The wildcard is useful to remove the incompatibility between different instantiations of a generic type.
It's invalid syntax to instantiate a generic type with wildcards. The type List<? extends Number>
means a List
of some type that is or extends Number
. To create an instance of this type doesn't make sense, because with instantiation you're creating something specific:
new ArrayList<? extends Number>();//compiler:"Wait, what am I creating exactly?"
Generic types with wildcards only make sense for variables and method parameters, because this allows greater freedom in what can be assigned/passed into them.
//compiler:"Okay, so passing in a List<Integer> or a List<Double> are both fine"
public void eatSomeNumbers(List<? extends Number> numbers) {
for (Number number : numbers) {
System.out.println("om nom " + number + " nom");
}
}
Make sure to keep in mind the limitations that come with using wildcards.
List<? extends Number> numList = ...
numList.add(new Integer(3));//compiler:"Nope, cause that might be a List<Double>"
As for your first example, the diamond is a new feature in Java 7 that allows the compiler to infer the type of the new generic instance, based on the type of the variable it's assigned to. In this case:
List<? extends Number> x = new ArrayList<>();
The compiler is most likely inferring new ArrayList<Number>()
here, but what's inferred hardly matters, as long as it's a valid assignment to the given variable. This was the reason for the diamond operator being introduced - that specifying the generic type of a new object was redundant, as long some generic type would make it a valid assignment/argument.
This reasoning only makes sense if you remember that generics in Java are a purely compile-time language feature, because of type erasure, and have no meaning at runtime. Wildcards exist only because of this limitation. By contrast, in C# generic type information sticks around at runtime - and generic wildcards don't exist in that language.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With