Recasting the basics | Java | Generics, do you really master it

Recasting the basics | Java | Generics, do you really master it

A: Let s talk about some basics today, just talk about generics.

B: Well generics not been in use, very simple ah, the pair <>will be able to specify generic, then use.

A: Then you talk about generics first, right?

B: Emmm... (For a while, although I have been using it all the time, but for a while, I cannot organize the language)

A: The concept is unclear, and the consciousness is vague, just staying at the stage of being able to use it, but not knowing the essence.

B: (Pretending to forget for a while)

A: Since generics are often used, you should know the generic erasure mechanism.

B: I know this, it is very simple, in order to be compatible with the previous version of JDK5.

A: Since there is a generic erasure mechanism when compiling, after compiling and packaging, for example, how can Gson correctly parse into the specified generic class? According to what I said earlier, generics are erased.

B: Emmm... Thoroughly cover the circle

As a wild Android bricklayer, the most commonly used one is that in all architectures, it is the core, so it is the foundation to the high-end. Even if you are proficient in calling third-party frameworks, you are just a proficient API caller. If a problem occurs, it will not be solved. You will only give it to the third party library, but the problem still cannot be solved. Therefore, on the road to growth, we must first thoroughly understand and master it . Only in this way can we avoid difficulties in the later stages.

1. First understand what generics are

Look at the Wikipedia definition:

There are two main types of generic definitions:

  1. In the program code, some types contain type parameters, that is to say, generic parameters can only represent classes, not individual objects. (This is the common definition today)
  1. Some classes that contain parameters in the program code. Its parameters can represent classes or objects, etc. (Now people mostly call this a template)

Before making the definition, let's learn about the parameter classification. In general, the parameters are divided into the following two categories:

  1. Formal parameters: input is a value;
  2. Type parameter: input is type;

Next, a brief introduction to the analysis combined with the code (you can skip it if you have already understood it).

1.1 Formal parameters

At work, methods are often written, for example:

// a b  Java  
public int add (int a, int b) { 
	return a + b;
}
 

According to the nature of the method, a certain value of the passed object needs to be used for corresponding processing. This is the formal parameter, abbreviated ;

1.2 Type parameters

In addition to processing the required value, sometimes the type needs to be specified, for example:

List<String> list = new ArrayList<>();
 

In the <>middle, there is a type String , note that the type of yo, not the object, for reasons yet, think about it ... Think again, this String you instantiate yet, now understand the object and the type of difference, right , <String>the purpose is to tell the compiler, in this List, specify a Stringtype, can only Stringobject types are processed, do not believe you can calllist.add(0) to try and see will not complain, for reasons subsequent speaks.

From the foregoing, a generic type parameter .

2. Why use generics

  1. Perform stronger type checking at compile time;
  2. Eliminate type conversion;
  3. Able to implement general algorithms;

Next, explain step by step:

2.1 Perform stronger type checking at compile time

Talk is cheap, show me the code. First up a piece of poison code:

//  int  
List list = new ArrayList<>();
list.add(1);
list.add(2);
list.add(3);
list.add("4"); // 

for (int i = 0; i < list.size(); i++) {
	int num = (int) list.get(3);
	System.out.println("num is " + num);
}
 

Since there is no type constraint, the compiler will not report an error. However, when running, the program will directly crash and report ClassCastExceptionthat if this happens online, it is a production accident. So how can the compiler detects an error it, which the debut, and modify the code as follows:

//  int  
List list = new ArrayList<Integer>();
list.add(1);
list.add(2);
list.add(3);
list.add("4"); // 

int num4 = ist.get(3); // 
 

After the appointment type, list only add corresponding type, that is, the sample code Integer, if passed String, is not compile, so it will be wrong by a run-time error in advance for compile-time error when, and to cooperate in multiplayer , Don t worry about other friends adding wrong type objects.

2.2 Eliminate type conversion

List list = new ArrayList<>();
list.add(1);
int num = (int) list.get(3); // List   get  

List list = new ArrayList<Integer>();
list.add(1);
int num = list.get(0); //  `Integer` 
 

2.3 Able to implement general algorithms

Without generics before the addworld:

public double add (int a, int b) {
	return a + b;
}
public double add (float a, float b) {
	return a + b;
}
public double add (double a, double b) {
	return a + b;
}
public double add (long a, long b) {
	return a + b;
}
public double add (byte a, byte b) {
	return a + b;
}
public double add (short a, short b) {
	return a + b;
}
// 
 

You have to write 6 methods to achieve compatibility. The world after using generics:

public <N extends Number> double add(N a, N b) {
    return a.doubleValue() + b.doubleValue();
}
 

Due to the use of generics, the number of methods is reduced to one, which realizes code reuse. Therefore, mastering generics is the foundation of the road to higher levels.

3. Generic classification

  1. Generic class
  2. Generic interface
  3. Generic method
// 
public class Box<T> {
	public T t;
}
 
// 
pubclic interface Box<T>{

}
 
// 
public <T> void test(T t) {

}
 

4. Commonly used type parameter names

  1. E --> Element
  2. K --> Key
  3. N --> Number
  4. T --> Type
  5. V --> Value
  6. S, U, V etc --> 2nd, 3rd, 4th types

It's just some consensus, which means what you specify, and you just use it. If you want to be maverick, then... it's not impossible.

5. Generic extension

In a lot of demand, the generic <>type in may not be a certain type, can not fail to specify a specific type, such as a parent of a class, so that subsequent extensions do next, the answer is yes, the next will be introduced look and , so that generics are also scalable.

5.1 Inheritance

5.1.1 Interface inheritance

interface GenericInterface<T>

/**
 *  
 */
interface GenericChildInterface1<T> : GenericInterface<T>

/**
 *  
 */
interface GenericChildInterface2 : GenericInterface<String>
 

It's relatively simple, so I won't introduce it too much.

5.1.2 Class inheritance

open class GenericClass<T>

/**
 *  
 */
class GenericChildClass1<T> : GenericClass<T>()

/**
 *  
 */
class GenericChildClass2 : GenericClass<String>()
 

It's also relatively simple, just take a look.

5.1.3 Multiple inheritance

open class A
open class B

interface C
interface D

/**
 *  
 *   A B   class  class 
 */
class MultipleGenericClass1<T : A, B, C, D>

/**
 *  
 *   class  class  
 */
class MultipleGenericClass2<T : C, A, D>

/**
 *  
 */
class MultipleGenericClass3<T : A, C, D>
 

Just look at the notes.

5.2 Wildcard

5.2.1 Wildcards controlled by upper limit

Syntax format: <? extends XXX>

Advantages: Expand the scope of compatibility.

List<Number> list1; //list1   Number  
List<? exntends Number> list2; //list2   Number  
 

5.2.2 Wildcards controlled by the lower limit

Syntax format: <? super XXX>

Advantages: expand the scope of compatibility; can establish the connection between generic classes and interfaces.

List<Integer> list1; //list1   Integer  
List<? super Integer> list2; //list2   Integer /
 

5.2.3 Unrestricted wildcards

Syntax format: <?>

Key use occasions:

  1. A method, and the implementation of this method can use the functions provided in the Object class. When the method in the generic class does not depend on the type parameter, such as the List.size() method, it does not care about the specific type of the elements in the List;
  2. List<XXX> is a subtype of List<?> Understand the difference between List<Object> and List<?>: the difference is NULL processing, the former does not support, while the latter can accept a null into the table;

Generic classes/interfaces between, there is no correlation, for example, List<Integer>and List<Number>no association, but you can use wildcards to establish contact.

Wildcards can only be used in generic methods, not generic classes and generic interfaces.

Wildcards can only be used in generic methods, not generic classes and generic interfaces.

Wildcards can only be used in generic methods, not generic classes and generic interfaces.

The important situation is said 3 times.

6. PESC principle

Benefits of the PESC principle: Improve the flexibility of the API.

6.1 PE (Producer extends)

If you only need to get the type T from the collection, use the <? extends T> wildcard.

public void test(List<? extends Number> list) {
    list.add(0); //    list  
}
 

6.2 SC (Consumer super)

If you only need to put the type T in the collection, use the <? super T> wildcard.

public void test(List<? super Number> list) {
    list.add(0); //    list  
}
 

Of course, if you want to be , and you want to be , then you don t need to use wildcards.

7. Type erasure

Talking about before, let's look at history. Generics are only appeared in JDK5, in order to ensure that older versions of the JDK normal running, only to fenced, and processed into a low version information can be identified, we can see, Java/Kotlin in as pseudo-generic .

So, how is it compatible? It was born in this scene.

Wipe rules:

The compiler will replace all the type parameters in the generic type with their upper (lower) limits. If there are no restrictions on the type parameters, then they will be replaced with the Object type. (Therefore, the compiled bytecode only contains regular classes, interfaces and methods.)

Insert type conversions when necessary to maintain type safety.

Generate bridge methods to maintain polymorphism when extending generics.

The bytecode is as follows, if you are interested, you can study it:

//  Integer  setT   Integer   
public getT()Ljava/lang/Integer;
@Lorg/jetbrains/annotations/Nullable;() //invisible
L0
LINENUMBER 5 L0
ALOAD 0
INVOKESPECIAL com/wd/kt/generic/Box.getT ()Ljava/lang/Object;
CHECKCAST java/lang/Integer
ARETURN
L1
LOCALVARIABLE this Lcom/wd/kt/generic/IntegerBox; L0 L1 0
MAXSTACK = 1
MAXLOCALS = 1

//  bridge  
//access flags 0x1041
public synthetic bridge getT()Ljava/lang/Object;
L0
LINENUMBER 3 L0
ALOAD 0
INVOKEVIRTUAL com/wd/kt/generic/IntegerBox.getT ()Ljava/lang/Integer;
ARETURN
MAXSTACK = 1
MAXLOCALS = 1
 

8. Restrictions and limitations in generics

8.1 Cannot instantiate type variables

/**
 *  
 *  
 */
private var t:T = T()
 

8.2 Type variables cannot be referenced in static domains

/**
 *  
 *  
 */
 private var t = T()
 

8.3 Type variables cannot be referenced in static methods

/**
 *  
 *  
 */
fun test(t: T) {

}

/**
 *  
 */
fun <T> test(t: T) {
    
}
 

8.4 The basic type cannot be used as a generic instantiation type, and the packaging type can

val list = arrayListOf<double>() // 
val list = arrayListOf<Double>() // 
 

8.5 Generics cannot use instanceof

At the grammatical level, the reason is the generic erasure mechanism, which does not exist in the compiler, so instanceof is invalid.

8.6 Two generic classes are the same

val boxInt = Box<Int>();
val boxString = Box<String>();
System.out.println(boxInt.class == boxString.class); //true
 

Because of the generic erasure mechanism, after the generic is erased, the types of boxInt and boxString are the same, and both are of the Box class.

8.7 Generics can declare arrays, but cannot initialize generic arrays

Box<Double>[] boxs; // 
boxs = new Box<Double>[10]; // 
 

8.8 Generics cannot extend Exception/Throwable*/

public <T extends Throwable> void test(T t) {
	try {
	
	} catch (T t) { // 
	
	}
}

// 
public <T extends Throwable> void test(T t) throws T {
	try {
	
	} catch (Throwable e) {
		throw t;
	}
}
 

8.9 Arrays in Java cannot use generics

/**
 *  
 *   Java  
 */
Arrays arrays = new Arrays<String>();
 

8.10 What is the difference between List and List <?> in Java

List: primitive type, not generic, so type checking is not involved;

List <?>: Generic, involving type checking;

8.11 What is the difference between List<Object> and List<?> in Java

List<?>: It is an unknown type List, you can assign List<String>, List<Integer>, etc. to List<?>;

List<Object>: It is any type of List, but you cannot assign List<String>, List<Integer>, etc. to List<Object>;

9. Generic differentiation

First define a generic class.

public class TestBox<T> {

    public T t;

    public T getT() {
        return t;
    }

    public void setT(T t) {
        this.t = t;
    }

}
 
public <T> void test() {
    // 
    TestBox box1 = new TestBox();
    //signature Lcom/wd/generic/TestBox<Ljava/lang/Object;>;
    //declaration: box2 extends com.wd.generic.TestBox<java.lang.Object>
    TestBox<Object> box2 = new TestBox<>();
    //signature Lcom/wd/generic/TestBox<*>;
    //declaration: box3 extends com.wd.generic.TestBox<?>
    TestBox<?> box3 = new TestBox<>();
    //signature Lcom/wd/generic/TestBox<TT;>;
    //declaration: box4 extends com.wd.generic.TestBox<T>
    TestBox<T> box4 = new TestBox<>();
    //signature Lcom/wd/generic/TestBox<+TT;>;
    //declaration: box5 extends com.wd.generic.TestBox<? extends T>
    TestBox<? extends T> box5 = new TestBox<>();
    //signature Lcom/wd/generic/TestBox<-TT;>;
    //declaration: box6 extends com.wd.generic.TestBox<? super T>
    TestBox<? super T> box6 = new TestBox<>();
}
 

10. Generics and reflection

Earlier we talked about generic erasure, so how can our commonly used frameworks such as Gson and FastJson be correctly converted into the generics we need?

In fact, although the generics are erased, we can see from the bytecode that the key information is still retained:

signature: specifies the type corresponding to the generic;

declaration: declares the original code information;

Since the information is retained, when you use it, you can get the information through special means, then you can continue to use generics normally, add it and combine the code to see how it is implemented.

class Test {

    val map = HashMap<String, Number>()

}

fun main() {
    val field = Test::class.java.getDeclaredField("map") //  field
    val paramType = field.genericType as ParameterizedType //  ParameterizedType ParameterizedType  
    for (item in paramType.actualTypeArguments) { // 
        println(item.typeName)
        // 
        //java.lang.String
        //java.lang.Number
    }

}
 

It can be seen that although the generic type is erased, it does not prevent the actual type information of the generic type from being obtained at runtime. There are always methods, and you may be rewarded by studying it.

Finally, the sample project address information sample code , welcomed the exchange of learning, if wrong, please correct me.