Java generic types cannot convert List <Integer>
types to List <Number>
types.
Different types
//If the OK type parameters are the same
List<Integer> foo = new ArrayList<Integer>();
// NG List<Integer>Is a List<Number>Cannot be converted to
List<Number> bar = foo;
//Similarly, conversion is not possible even with NG initialization
List<Number> foo = new ArrayList<Integer>();
Therefore, by using ** boundary wildcard type **, you can declare ** type parameters with some width **. However, there are two types of boundary wildcard types. This is because when the type boundaries are set, the problem that the processing system cannot set the type occurs in each situation. Let's look at the problem one by one.
The following Fruit class is used for explanation.
Generic<? extends T>
If you specify a type parameter as above, the type parameter is ** capped wildcard type **. The ? extends T </ kbd> type means ** T or a type that represents all of its subclasses **.
Considering using the List <? Extends Fruit>
type, it is possible to convert from ** Fruit class or List type ** that specifies its subclass.
List that can be converted
List<? extends Fruit> basket = new ArrayList<Fruit>(); // 1
List<? extends Fruit> basket = new ArrayList<Melon>(); // 2
List<? extends Fruit> basket = new ArrayList<Lemon>(); // 3
Next, consider retrieving an element from the List <? Extends Fruit> basket
variable,
Please be aware that the basket variable can be retrieved as a Fruit class regardless of whether it is 1, 2, or 3.
Consider retrieving an element from a List <? Extends Fruit> basket
variable.
In the case of a wildcard type with an upper limit, it is ** at least a type that represents the T class and its subclasses **, so it is possible to specify the upper limit T when retrieving an element from the List.
Convert capped wildcard type to T type
List<? extends Fruit> fruits = ...
//no problem
Fruit fruit = fruits.get(i);
This is a conversion from ? Extends T
type to T
type.
Next, when you get the element from the basket variable, consider whether you can specify its subclass.
The List <? Extends Fruit>
type can be converted from at least the following three List types.
ArrayList<Fruit>
ArrayList<Melon>
ArrayList<Lemon>
Therefore, when getting an element, it is not possible to specify a subclass that derives from several series.
Uppered wildcard types cannot be converted to subclass types
List<? extends Fruit> basket = ...
//NG compilation error Cannot convert to Melon type
Melon fruit = basket.get(i);
Above, if the ? Extends Fruit
type can be converted to a subclass, the following contradictions will occur.
Convert capped wildcard type to T subclass type 1
//Make a melon list
List<Melon> melonList = new ArrayList<Melon>()
melonList.add(new Melon());
// List<Melon>List type melonList<? extends Fruit>Convert to type
List<? extends Fruit> basket = melonList;
//If it can be converted to a subclass, it will be possible to receive it as Lemon.
Lemon fruit = basket.get(i);
Therefore, conversion to a subclass is not allowed.
Therefore, it is not possible to convert from a ? Extends T
type to aT subclass
type.
Next, consider adding an element to the List <? Extends Fruit> fruits
variable.
Since you can convert from ? Extends Fruit
type to Fruit type
, it seems that you can add a Fruit object to the following fruits variable.
Write to capped wildcard type
List<? extends Fruit> fruits = new ArrayList<Fruit>();
//Seems possible at first glance
fruits.add(new Fruit());
However, if the above is allowed, the following contradictions will occur.
Writing to a capped wildcard type is inconsistent
//The reality of fruits is a melon list
List<? extends Fruit> fruits = new ArrayList<Melon>();
//The Lemon class is a subclass of the Fruit class
Fruit fruit = new Lemon();
//Lemons can be added to the melon list
fruits.add(fruit);
Therefore, it is not possible to convert from the concrete type T
type orT subclass
type to? Extends T
type.
This means that if you declare a List type with a capped wildcard, you cannot add elements.
That's where another boundary wildcard type comes in.
Generic<? super T>
If you specify a type parameter as above, it is ** a wildcard type with a lower bound **. The ? super T </ kbd> type means ** T or a type that represents all of its superclasses **.
You can convert to ** List \ <? Super Melon > ** type from ** Fruit class or List type ** whose type parameter is its superclass.
List that can be converted
List<? super Melon> baskets = new ArrayList<Melon>(); // 1
List<? super Melon> baskets = new ArrayList<Fruit>(); // 2
List<? super Melon> baskets = new ArrayList<Food>(); // 3
List<? super Melon> baskets = new ArrayList<Object>(); // 4
This time around, I'll think about adding elements to the basket variable first, Please be aware that Melon class can be added to the basket variable in 1 to 4 when it is 1, 2, 3 or 4.
To add an element to a List <? Super Melon> basket
variable, the wildcard type with a lower bound is ** a type that represents at least a T class or higher superclass **, so the element is added to the List type. When adding, you should be able to specify its lower bound, T.
Check that you can add one by one by referring to the example.
When List <? Super Melon> fruits = new ArrayList <Object> ()
Add to Object list
// OK
fruits.add(new Melon())
// OK
fruits.add(new WaterMelon());
When List <? Super Melon> fruits = new ArrayList <Food> ()
Add to Food list
// OK
fruits.add(new Melon())
// OK
fruits.add(new WaterMelon());
When List <? Super Melon> fruits = new ArrayList <Melon> ()
Add to Melon list
// OK
fruits.add(new Melon());
// OK
fruits.add(new WaterMelon());
That is, it is possible to convert from a T
or T subclass
type to a ? Super T
type.
The ? super Melon </ kbd> type is a "type that represents the Melon class and all of its superclasses".
Also, all Java classes inherit from the ** Object ** type.
Therefore, conversion from ? Super T
type to ʻObject` type is possible.
Conversion from wildcard type with lower limit to Object type
Object object = basket.get(i)
Boundary parameter types can be used to represent a wide range of generic type types.
Boundary wildcard type and concrete type conversion table with cap
Yes / No | Conversion source | Conversion destination | Its meaning |
---|---|---|---|
OK | ? extends T |
T |
T Can get type value |
NG | T |
? extends T |
Cannot write |
Boundary wildcard type with lower bound and concrete type conversion table
Yes / No | Conversion source | Conversion destination | Its meaning |
---|---|---|---|
OK | ? super T |
Object |
Value can be obtained as Object type |
OK | T |
? super T |
T-type value can be written |
OK | T subclass |
? super T |
Can write subclass type values of T |
Generally, there is a spell called ** PECS **, and the bounded wildcard type with an upper limit is sometimes called Producer, and the bounded wildcard type with a lower limit is sometimes called Consumer.
Producer-Specializing in value generation
Consumer-Specialized in receiving values
Appendix
You may have heard that when you specify a bounding wildcard type with an upper bound, the List cannot be written.
This simply means that you cannot:
** Cannot convert concrete type
to capped wildcard type
**
If you declare a generics type object with a capped wildcard, the above conversion will occur when you call a function with that type parameter.
Consider the Stack \ <T > class below.
Stack class
class Stack<T> {
void push(T e) {}
}
Declare the Stack \ <T > class with a capped wildcard type.
Stack<? extends Fruit> basket = new Stack<Fruit>();
At this time, the type parameter T of the basket variable is of type ? Extends Fruit
.
So the push method is equivalent to:
When the Stack type parameter is a capped wildcard
void push(`? extends Fruit` e) {}
Therefore, calling this push method results in a conversion from a Fruit
to a? Extends Fruit
type.
And this conversion is NG.
//NG Fruit type`? extends Fruit`Cannot be converted to type
basket.push(new Fruit());
Recommended Posts