Small knowledge, big challenge! This article is participating in the creation activity of “Essential Tips for Programmers”.
Hello, everyone, I am fangyuan, this blog is my reference to a lot of Java generics upper and lower bound knowledge to complete the draft, so after reading this article, there is really nothing!
1. Preparation
- There are the following categories
Inheritance relationships
“, prepare for the following understanding
2. What is the problem with upper and lower bounds without generics?
Apple is a Fruit subclass, so you can’t go wrong with this code
// ok
Fruit apple = new Apple();
Copy the code
What if we wrote code like this, and defined a List that had Fruit in it, and assigned it a List that had Apple in it?
List<Fruit> plate = new ArrayList<Apple>();
Copy the code
ArrayList
cannot be converted to java.util.List
, obviously there is no inheritance reference between collections
So in the face of the above problems, you need to enter the upper and lower bounds
3. Upper bounds of generics,? extends T
Use the upper bounds of generics,? Extends Fruit solves that problem
List<? extends Fruit> plate = new ArrayList<Apple>();
Copy the code
- So? How do we understand the upper bound?
? Is a Java wildcard, in the example above, upper bound? extends Fruit
Represents any subclass of Fruit (including Fruit itself) that contains these elements in the collection
- What’s in the upper bound
The characteristics of
?
You can only fetch, you can’t store, but let’s just say you can only fetch, and that makes a lot of sense, because we’re all subclasses of Fruit, so every element that we pull out of our collection must be referenced by Fruit, and you can see this code
List<Apple> appleList = new ArrayList<>();
appleList.add(new Apple());
appleList.add(new Apple());
List<? extends Fruit> plate = appleList;
Fruit fruit = plate.get(0);
Copy the code
How to understand if you can’t save? Didn’t you say the list is full of Fruit subclasses? So why can’t we just throw in any subclass of it?
Just listen to me slowly!
This code is redlined and does not allow additions, as shown in the figure below
// The generic is Apple
List<Apple> appleList = new ArrayList<>();
List<? extends Fruit> plate = appleList;
Copy the code
So let’s think about it a little bit. Now the plate refers to this appleList, and the appleList has a generic type of Apple, so that means the plate has a generic type of Apple, so there’s no problem adding an Apple element to it, There is no problem with adding RedApple and GreenApple, both of which can be safely converted to Apple
But! Come back, let’s see,? “Extends Fruit” is any subclass that extends from “Fruit.” Okay, so Banana is also a subclass of “Fruit,” but no! Remember, we’ve got the plate pointing to the appList, and the appleList says the generic is Apple, Banana adds it to it, can it convert to Apple? Obviously not, but they’re all Fruit subclasses, which is to say, in order to be absolutely safe, you can’t add in the upper bound, so you don’t have a transition failure problem
Here’s another interesting situation
List<? extends Fruit> plate = Arrays.asList(new Apple(), new Banana());
Fruit apple = plate.get(0);
Fruit banana = plate.get(1);
Copy the code
In this case, the plate really becomes a plate that can hold any kind of Fruit, you have an Apple and a Banana inside, and when you take it out, it really is Fruit
4. The lower bound of generics,? super T
- The lower bound of the generic type
? super Fruit
For example, it represents any parent of Fruit, including Fruit itself (and Object).
-
What is the character of the lower bound? Can save, in fact also can take, why say actually can also take, because I read some articles, the upper and lower bounds to distinguish, make them exactly the opposite, the characteristics of both the characteristics of the lower bound is can’t get into it in the code of practice, can come out, only will disable the element type, element type is to take out the Object
-
First to explain the memory, look at the following code, notice that its lower bound is Apple
// Defines a plate whose lower bound is? super Apple
List<? super Apple> plate = new ArrayList<>();
plate.add(new Apple());
plate.add(new RedApple());
plate.add(new GreenApple());
Copy the code
This code in the compiler is no problem, no error, it works, there are some students have a problem, the lower bound is any type of parent, so why not add Apple’s parent to it? Didn’t we say we could store elements in it? ! Why did you return the red line?!
Let’s first think about the absolute security mentioned above, will there still be the failure of the transformation mentioned above? Let’s see? The scope of Super Aplle is shown below
Ok, it’s ok to add Apple, it’s ok to add Fruit and Food, they’re all in the lower bounds, but who’s to say that you’re adding those types of elements? Who can guarantee its absolute safety? Can’t you? ! I add Banana to it and it’s going to go wrong!
So why add Apple and its subclasses, because it’s absolutely safe, these can be safely converted to Apple, there’s nothing wrong with it, there’s nothing wrong with going up, so it can be added, and the lower survivable element is this reflection
- Just a little bit more about what is that
You can actually take it
But all the objects are objects, which invalidates the type of the element, and because? The scope of super Apple is too large, I don’t know what type it is, so I can only use the highest Object to reference it
5. How can it be used? What are PECS principles?
- Let’s define a
MyStack
, and added onePushAll method
Pushes all elements of the List collection passed in to the stack, but it is worth noting that argumentsList<E> fruits
Upper and lower bounds are not used
public class MyStack<E> extends Stack<E> {
public void pushAll(List<E> fruits) {
for(E fruit : fruits) { push(fruit); }}}Copy the code
Java: incompatible type Java.util.List
can’t be converted to java.util.List
The following
public class MyStack<E> extends Stack<E> {
public void pushAll(List<? extends E> fruits) {
for(E fruit : fruits) { push(fruit); }}}Copy the code
The code problem is gone and it works, and it can get the stack elements out
- So with the pushAll method, I’m going to write a corresponding one
PopAll method
, as follows, there is no provisionLower bound
oh
public void popAll(List<E> fruits) {
while (!isEmpty()) {
fruits.add(pop());
}
}
Copy the code
Java: incompatible type Java: incompatible type Java: incompatible type Java: incompatible type Java: incompatible type Java: incompatible type Java: incompatible type Java: incompatible type Java.util.List
cannot be converted to java.util.List
We modified the code to add upper and lower bounds, and the code did not report errors and worked properly
public void popAll(List<? super E> fruits) {
while (!isEmpty()) {
fruits.add(pop());
}
}
Copy the code
- The principle of PECS
How to understand producer and consumer,? Extends E always takes elements out of itself and makes those elements available to the stack, which we call producers; ? Super E, on the other hand, is constantly adding to the set, as if everything given to it is consumed by it, so we call it a consumer
So what are PECS principles?
Producer Extends, Consumer Super. That is, if a parameter type is Producer, we use? Extends T. If a parameter type is a consumer, what is the upper bound of extends T? Super T lower bound
Shoulders of giants
- Java generics <? Super T> super. How is it different from extends?
- PECS rules with extends and super keywords
- Effective Java (3rd edition) article 31
- JAVA wildcard –? Extends T,? Super T (Angle brackets are not allowed to type, no, no, no)