The introduction
Error: Capture of? Error: Capture of? Error: Capture of? Extends…). ; The method returns Object, but uses generics.
Class inheritance relation
For the convenience of explanation, the inheritance relationship of all classes in the article is shown in the figure
Introduction to Java generic wildcards
and
is a Wildcard in Java generics.
- : refers to “Upper Bounds Wildcards”
- : Means “Lower Bounds Wildcards”
When using the
wildcard defines a variable that makes it impossible to use methods with generic parameters such as:
ArrayList<Apple> apples = new ArrayList<>();
apples.add(new Apple());
ArrayList<? extends Fruit> fruits = apples;
// If the method returns a generic value, it can be used normally
Fruit fruit = fruits.get(0);
// If the method parameter contains generic parameters, the compiler will report an error
// fruits.add(new Fruit());
// Method parameters do not contain generic parameters, can be used normally
fruits.remove(0);
Copy the code
When using the
wildcard defines a variable in which any method whose return value is generic will return Object
ArrayList<Fruit> fruits = new ArrayList<>();
fruits.add(new Apple());
ArrayList<? super Apple> apples = fruits;
// Methods whose return value is generic only return type Object
Object object = apples.get(0);
// But there is no problem with adding, you can add the type needed by Apple and its subclasses
apples.add(new Apple());
apples.add(new RedApple());
apples.add(new GreenApple());
// apples.add(new Fruit()); // The parent class cannot be added
Copy the code
There are reasons for limitations
So why did Java add this restriction? It can’t be idle.
First let’s make it clear that Java is not supportedCovariantOne may ask what is a thing at this time?
Look at the detailsKotlin’s tutorial on generics
Here we simply understand that in JavaList<Apple>
notList<Fruit>
The subclass,
The next two lines of code directly report an error
But the actual coding process does have this requirement, and Java provides us with the
and
.
<? Extends T> upper bound wildcard
is used to identify the type of a variable. The extends T> identifies values that accept generic T and its subclasses. As in the above example, we can modify this to ArrayList\
fruits does not error
ArrayList\<? extends Fruit\> fruits
It could actually be an array of the following generics
Now that we can explain why Java disallows calls to methods with generic parameters, imagine what’s wrong with the following code
ArrayList<Apple> apples = new ArrayList<>();
ArrayList<? extends Fruit> fruits = apples;
fruits.add(new Banana()); // It doesn't actually compile, assuming it does
Apple apple = apples.get(0);
Copy the code
That’s right, the program will get an error when it runs to line 4, because Banana can’t be strongly converted to Apple, because ArrayList\
fruits can refer to arrays of different generics. Java prevents this by simply disallowing calls to methods with generic arguments. However, there is no restriction on methods whose upper bound operators return generic values because fruits refer to their subclasses. There is nothing wrong with moving up
<? Super T> lower bound wildcard
is used to identify the type of a variable that accepts values of the generic T and its parent class. Suppose we define a variable: ArrayList
apples = fruits; , then FRUITS can actually refer to the generic array as shown below:
In this case there is no restriction on the addition of elements, because fruits must refer toFruit
An array of parent generics, there’s no problem adding a Fruit subclass to such an array,
There is a problem with getting elements from fruits, however, because you don’t know that FRUITS refers to an array of generic type,
Is the ArrayList < Fruit >? , ArrayList<Food>(), and even ArrayList<Object>() are possible
In this case, to ensure correctness, the Object removed from fruits is of type Object
Reference: Kotlin b site up parabola on Kotlin generics explained