This is the third day of my participation in the November Gwen Challenge. Check out the details: the last Gwen Challenge 2021
Generics are a fundamental feature in Java, but there are some concepts that can be confusing for a while.
This article may help you answer the following questions.
- What about generic erasure?
- How is the inheritance of generic classes broken?
- What about PECS principles?
- How does generic erase get the type of a generic?
1) concept
1.1) Simple understanding of generic concepts
Generics: parameterized types, also known as parameterized types. By contrast, when we define a method in Java, we define its method parameters, method return value, method name, access control character, method body, and so on. Such as
public String fetchLabel (int number){ return count + ""; }//code_1
fetchLabel(1)//code_2
Copy the code
Where, int number at code_1 is the method parameter, and a parameter named number of type int is defined, which is the method parameter. The method called at code_2 passes in the specific argument 1, which is the actual argument to the method. Similarly, the exact type specified by the original specific type is parameterized. As shown in the following code Code_3, the original attribute data type of Response class is the exact type Student, which is written as a parameterized type similar to the parameter T of the method. When used, the concrete type Student is passed, like the argument to the method. This implements parameterization of the type.
class Response { Student data; }//code_3
class Respons<T>{ T data; }//code_4
Copy the code
1.2) Related terms
To describe aspects and further understand the concepts associated with generics, the following uses List as an example to illustrate terms related to generics
- List: Raw types
- List, List Generic Types
- Where E is the formal type parameter
- Is a restricted type parameter
- The List, the List <? >, List <? Exetends Number> Parameterized Type
- Integer is the actual parameter type, which can also be called a type argument
- ? Infinite collocation type
- Is the finite-pass matching type
The above is a popular understanding of the concept of generics, as for the usual use here we do not repeat. But a lot of times we run into some weird and counterintuitive problems with generics, so let’s talk about that in more detail.
2) Types of disappearance
//code_5
Response<Student> studentResponse = new Response<>()
Response<Apple> appleResponse = new Response<>();
Class stuClz = studentResponse.getClass()
Class appleClz = appleResponse.getClass()
System.out.println(stuclz == appleClz) //true code_6
Copy the code
The code_5 code above ends with code_6 true. Yi! Generic types are gone
The introduction of generics in the Java language provides strict compilation checks, but the compiler will have the feature of type parameter erasure, and the compiler will perform type erasure for generics mainly reflected in three aspects:
- Generic classes and generic interface types are erased
- Generic method type erasure
- Generate bridge methods
2.1) Generic classes and generic interface types are erased
The actual type in the parameterized class is erased. Replace the actual type argument with a boundary type or Object. Generics information is not retained at run time. Such as:
class Box<T> {
T item;
public void setItem(T item) {
this.item = item; }}// After the generic type is erased
class Box {
Object item;
public void setItem(Object item) {
this.item = item; }}Copy the code
2.2) Generic method type erasure
The actual type parameters in the generic method are erased
/ / code
public static <T> printProductInfo(T product){
System.out.println(e.toString())
}
// After type erasure during compilation
public static printProductInfo(Object product){
System.out.println(product.toString())
}
Copy the code
2.3) Generic erase effects and bridging methods
Automatically generates the bridge method because of the nature of generic erasure
class Box<T> {
T item;
public void setItem(T item) {
this.item = item; }}class MyBox extends Box<Integer>{
@Override
public void setItem(Integer item) {
super.setItem(item); }}// After type erasure
class Box {
Object item;
public void setItem(Object item) {
this.item = item; }}class MyBox extends Box<Integer>{
Object t;
@Override
public void setItem(Integer item) {
super.setItem(item); }}Copy the code
MyBox is a subclass of Box, and the setData() Method signature is inconsistent between the subclass and the parent class, that is to say, Method overwrite is invalid. In order to solve this problem and protect Java polymorphism, the Java compiler introduces Bridge Method. The Bridge Method is generated in a subclass of the generic. This way the setItem() method of the subclass and its parent class have the same signature.
class MyBox extends Box<Integer>{
Object t;
public void setItem(Object item) {// Bridge method: Ensures that it has the same signature as the parent method, protecting Java polymorphism
setItem((Integer)item);
}
public void setItem(Integer item) {}}Copy the code
3) Counterintuitive questions
Is List a subtype of List?
- Although Integer is a subclass of Number, List and List are not child parents. This means that the following code will not compile
public void containerOpt(List<Number> numbserList){ /* todo */}
containerOpt(new ArrayList<Integer>())
Copy the code
-
In addition, generics have subclassing rules. List is a subclass of the ecotype List, but not a subclass of List. The following code, which passes the compile check but is prone to run-time exceptions, was introduced after Java1.5 and is not recommended to use naked native types.
List rawList = new ArrayList<String>()// The compiler check will not report an error Copy the code
-
There are always classes, and sometimes we have to use native types, mainly in two cases
- When we need to fetch classes, such as list. Class, there is no problem. But list. class is not allowed
- When we use instanceIOf to determine the type
- List
Type parameter type, generic type declaration, can reference any type of List. Like List,List,List. With this declaration reference, we can call its get() method to get the roundness inside - List can be thought of as a plain Raw primitive type
- List, Object generic types of generic types for a particular type.
4) The solution — ride it
4.1) Generic wildcards, wildcards with boundaries PECS rules
4.1.1) Upper boundary wildcards? extends T
- List
means that the object type in the collection is a subclass of T. - Based on the first point, we can take objects from this collection as type T. Either class T or a subclass of it is considered to be of type T.
- The type of the object in the collection is a bounded range T or a subclass of T. The superclass refers to the subclass object. Subclasses cannot directly refer to the superclass object and therefore cannot add objects to the container in the boundary.
4.1.2) Lower boundary wildcards? super T
- List
indicates that the element in the List is a superclass of T, but it is not known which superclass. - Based on that, you can add T and T subclasses to the set. Because both T and T subclasses can be considered superclass types of T and T.
- The type of the object in the collection is a bounded range T or the superclass of T cannot take an element, not knowing which superclass is inside the container. Can confirm that type T must be a descendant of the type in the container.
4.1.3) PECS
PECS(Producer Extends Consumer Super) is exactly the same rule as described above
- When we use the upper boundary? With extends T, we can only ensure that objects in the container inherit from T or from the T class itself. Therefore, all the data we can take out of the set is of type T. In this case, the container provides objects and acts as a Producer. Therefore, it is called [PE] Producer Extends;
- When we use the lower boundary? Super T ensures that all objects in the container are superclasses of T. So we can put T or subclasses of T in containers. In this case, the container absorbs objects inward, acting as the Consumer, so it is called [CS] Consumer Super.
Refer to the code above for details.
// Method 1: private static void producer(List<? extends Exception> exceptions) { Exception exception = exceptions.get(0); Exception exception1 = exceptions.get(1); } // Method 2: private static void consumer(List<? super RuntimeException> runtimeList) { runtimeList.add(new IllegalArgumentException()); runtimeList.add(new RuntimeException()); runtimeList.addAll(new ArrayList<RuntimeException>()); } // Method 3: private void doSomething(a) { //1. Declare references List<? extends RuntimeException> runtimeExceptionList = new ArrayList<IllegalArgumentException>(); List<? super Exception> exceptionList = new ArrayList<Throwable>(); / / 2. The refs //2.1 Production superclass producer(new ArrayList<RuntimeException>()); producer(new ArrayList<IllegalArgumentException>()); producer(new ArrayList<Exception>()); //2.2 Consume superclasses consumer(exceptionList); consumer(new ArrayList<Throwable>()); } Copy the code
4.2) Escape type erasure
-
As described in Section 2, run-time generic types are erased, such as List run-time types erased to List. We need to know the exact type when deserializing JSON string-to-object data. emm… I don’t know which will grow old first, the universe or trouble
-
Recalling deserialization when using third-party libraries such as Gson, we were able to retrieve the exact type of the generic class through TypeToken and deserialize the data.
-
The secret is that a subclass of a generic class can call getGenericSuperclass () to get the ParameterizedType of its parent class. GetGenericSuperclass returns the Type of its parent class. To get the real parameters. Although the type is erased, its true type can be found in the bytecode. Specific can consult stackoverflow.com/questions/9…
-
It’s also worth noting that when we use TypeToken, we need to implement a subclass and we usually say new TypeToken(){} which is actually implementing an object that inherits the anonymous Class TypeToken, GetGenericSuperclass () can then be used to retrieve the parent class information inside the anonymous TypeToken object.
-
Docs.oracle.com/javase/tuto…
-
Stackoverflow.com/questions/9…
5)”
This article discusses the problems related to Java generics from the concept of Java generics to the features to some problem solutions.
Shortcomings, not stingy criticism and correction.
Reference:
- List