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 categoriesInheritance 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 FruitRepresents any subclass of Fruit (including Fruit itself) that contains these elements in the collection

  • What’s in the upper boundThe 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 FruitFor 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 thatYou 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 aMyStack, and added onePushAll methodPushes all elements of the List collection passed in to the stack, but it is worth noting that argumentsList<E> fruitsUpper 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 onePopAll method, as follows, there is no provisionLower boundoh
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)