Small knowledge, big challenge! This article is participating in the creation activity of “Essential Tips for Programmers”.
preface
In the last article we looked at what generics are and how they can be used. To review: As long as there are no warnings at compile time, do classcastExceptions occur at runtime
This time, I’m talking about the PECS principle, which is Producer Extends Consumer Super
Introducing PECS let’s first look at what the state of generics is when compiled in Java
public class Test {
public static void main(String[] args) {
Class a = new ArrayList<Integer>().getClass();
Class b = new ArrayList<String>().getClass();
System.out.println(a == b);
}
}
打印结果为 true
Copy the code
As you can see from the above example, I marked array A as Integer and array B as String, but why are the outputs equal?
Because inside the generic code, you can’t get any information about the generic parameter types! Java generics are implemented using erasure. When you use generics, any information is erased and all you know is that you are using an object. So List< Integer> and List< String> are erased to their native List types at run time.
Generic erasure
The JVM does not know that generics exist because they are treated as ordinary classes and methods at compile time;
The handling mechanism is through type erasure, erasure rules:
- If the generic type does not specify a specific type, use Object as the primitive type.
- If there is a qualified type < T exnteds XClass >, use XClass as the original type.
- If there are more than one qualification < T exnteds XClass1 & XClass2 >, use the first bound type XClass1 as the primitive type;
The reason Java introduced generic erasers was to avoid creating unnecessary classes at runtime because of the introduction of generics.
The purpose of generic erasers is to make up for what was left out in the previous chapter. Today’s lesson is that PECS is about understanding the upper and lower limits of generics
Extends extends the upper limit of the generic
On the code to understand
public static void Test(WuLing car){
Wuling wuling = car.getCar();
}
public static void Test(WuLing<? extends Car> car){
Car wuling = car.getCar();
}
Copy the code
In two pieces of code, we can see that using the extends definition, we now have an upper limit, and when the receiving entity class returns, we can use all classes that inherit from Car.
Usually something like this, right? We call it a wildcard. Extends requires that the type of the generic type be an argument type, or a subclass of the argument type.
Take another example, or car to take the example of the largest category of vehicles – “Wuling Hongguang -” red Wuling Hongguang, now there is such a relationship. And generics? Extends Wuling Macro light, then wuling macro light and red wuling macro light can be received, but not the vehicle because the vehicle is larger than wuling macro light.
If you still don’t know what I suggest you do, draw the extended class as you did above. Then check to see who extends is. Then draw a line to split the extends object.
Extends does not add objects inside a method, that is, it cannot fill elements, because you do not know what the last delimiter of its generics is.
The lower limit for super generics
A super lower limit, as opposed to extends, requires that the type of the generic type be either an argument type or a parent of the argument type.
public static void Test(List<? super Car> car){
car.add(new Wuling());
}
Copy the code
A lower limit can add elements inside a method, because your lowest is car, and car subclasses are unambiguous so you can add elements, whereas extends doesn’t specify where you’re bounded so you can’t add elements.
conclusion
- Super is the opposite of extends
- Super is the upper limit of generics and extedns is the lower limit
- Generics exist before compilation, the primary user verifies compliance with the specification before compilation, and then disappears to primitive types