Java generic explanation, the most complete graphic explanation in history

Posted by deffe on Tue, 15 Feb 2022 01:42:12 +0100

Generics play an important role in java, which can be seen in both open source framework and JDK source code.

It is no exaggeration to say that generics are essential elements in general design, so it is a required course to truly understand and correctly use generics.

1: Generic essence

Java generics is a new feature introduced in JDK 5. Generics provides a compile time type safety detection mechanism, which allows programmers to detect illegal types at compile time.

The essence of generics is parameterized type, that is, assign a parameter to the type, and then specify the specific value of the parameter when using, so that the type can be determined when using. This parameter type can be used in classes, interfaces and methods, which are called generic classes, generic interfaces and generic methods respectively.

2: Why use generics

The advantage of generics is to check type safety at compile time, and all coercions are automatic and implicit, so as to improve the reuse rate of code.

(1) Ensure the safety of the type.

Before there is no generics, every object read from the collection must be type converted. If the wrong type object is accidentally inserted, the conversion processing will be wrong at run time.

For example: using a collection without generics:

public static void noGeneric() {
ArrayList names = new ArrayList();
names.add("mikechen Internet Architecture");
names.add(123); //Normal compilation
}

Use collections with generics:

public static void useGeneric() {
ArrayList<String> names = new ArrayList<>();
names.add("mikechen Internet Architecture");
names.add(123); //Compilation failed
}

With generics, add(123) will fail to compile the defined set names when compiling.

It is equivalent to telling the compiler what the object type received by each collection is. The compiler will do type checking during compilation to tell whether the wrong type of object is inserted, which makes the program safer and enhances the robustness of the program.

(2) Eliminate cast

A side benefit of generics is the elimination of many cast types in the source code, which makes the code more readable and reduces the chance of errors.
As an example, the following code segments without generics need to be cast:

List list = new ArrayList();
list.add("hello");
String s = (String) list.get(0);

When overridden to use generics, the code does not need to be cast:

List<String> list = new ArrayList<String>();
list.add("hello");
String s = list.get(0); // no cast

(3) It avoids unnecessary packing and unpacking operations and improves the performance of the program

In non generic programming, passing the barrel list type as an Object will cause Boxing and Unboxing operations, both of which have great overhead. After the introduction of generics, there is no need to perform Boxing and Unboxing operations, so the operation efficiency is relatively high. Especially in the system with very frequent collection operations, the performance improvement brought by this feature is more obvious.

Generic variables have fixed types. When they are used, they already know whether they are value types or reference types, avoiding unnecessary boxing and unpacking operations.

object a=1;//Because it is an object type, the packing operation will be carried out automatically.
 
int b=(int)a;//Forced conversion, unpacking operation. In this way, when the number of times is more, it will affect the operation efficiency of the program.

After using generics

public static T GetValue<T>(T a)
 
{
  return a;
}
 
public static void Main()
 
{
  int b=GetValue<int>(1);//When using this method, it has been specified that the type is int, so there will be no boxing and unpacking operations.
}

(4) It improves the reusability of code.

3: How to use generics

There are three ways to use generics: generic classes, generic interfaces, and generic methods.

1. Generic class

Generic class: defines a generic type on a class

Define format:

public class Class name <Generic type 1,...> {
    
}

Note: generic types must be reference types (non basic data types)

Define a generic class, add a pair of angle brackets after the class name, and fill in the type parameters in the angle brackets. There can be multiple parameters, which are separated by commas:

public class GenericClass<ab,a,c> {}

Of course, the following parameter types are also standardized, and cannot be as arbitrary as above. Generally, type parameters are expressed in uppercase single letters:

T: Any type
E: Type of element in the collection element
K: Key value form key
5: Key value form
Example code:

Generic class:

public class GenericClass<T> {
    private T value;
 
 
    public GenericClass(T value) {
        this.value = value;
    }
    public T getValue() {
        return value;
    }
    public void setValue(T value) {
        this.value = value;
    }
}

Test class:

//TODO 1: generic class
GenericClass<String> name = new GenericClass<>("mikechen Internet Architecture");
System.out.println(name.getValue());
 
 
GenericClass<Integer> number = new GenericClass<>(123);
System.out.println(number.getValue());

Operation results:

2. Generic interface

Overview of generic methods: define generics on methods

Define format:

public <generic types > Return type method name (generic type variable name) {
    
}

Key points:

    • The formal parameters defined in the method declaration can only be used in the method, while the type formal parameters defined in the interface and class declaration can be used in the whole interface and class. When the fun() method is called, the compiler will judge the actual type represented by the type parameter T according to the actual object passed in.
public interface GenericInterface<T> {
void show(T value);}
}
public class StringShowImpl implements GenericInterface<String> {
@Override
public void show(String value) {
System.out.println(value);
}}
 
public class NumberShowImpl implements GenericInterface<Integer> {
@Override
public void show(Integer value) {
System.out.println(value);
}}

Note: when using generic types, the generic types defined before and after must be consistent, otherwise compilation exceptions will occur:

GenericInterface<String> genericInterface = new NumberShowImpl();//Compilation exception

Or do not specify the type at all, so new can be of any type:

GenericInterface g1 = new NumberShowImpl();
GenericInterface g2 = new StringShowImpl();

3. Generic method

Generic method refers to the specific type of generic when calling the method.

  • Define format:

Modifier < variable representing generics > return value type method name (parameter) {}

For example:

/**
     *
     * @param t Parameters passed in generics
     * @param <T> Generic type
     * @return T The return value is of type T
     * explain:
     *   1)public < T > is very important in the middle of the return value, which can be understood as declaring this method as a generic method.
     *   2)Only methods that declare < T > are generic methods. Member methods that use generics in generic classes are not generic methods.
     *   3)<T>Indicates that the method will use generic type T. only then can generic type t be used in the method.
     *   4)Like the definition of generic class, t here can be written as any identification. Common parameters in the form of T and E are often used to represent generics.
     */
    public <T> T genercMethod(T t){
        System.out.println(t.getClass());
        System.out.println(t);
        return t;
    }
 
 
public static void main(String[] args) {
    GenericsClassDemo<String> genericString  = new GenericsClassDemo("helloGeneric"); //The generics here can be different from the generic methods called below.
    String str = genericString.genercMethod("hello");//The passed in is a String type, and the returned is also a String type
    Integer i = genericString.genercMethod(123);//An Integer type is passed in and an Integer type is returned
}
 
 
class java.lang.String
hello
 
 
class java.lang.Integer
123

It can be seen here that the types of generic methods are different with the types of our incoming parameters. Generic methods enable methods to change independently of classes.

4: Generic wildcard

The wildcard of Java generics is a special syntax used to solve the problem of reference passing between generics. It mainly includes the following three categories:

//The representation type parameter can be any type
public class Apple<?>{}
 
//The representation type parameter must be a or a subclass of A
public class Apple<T extends A>{}
 
//Indicates that the type parameter must be a or a supertype of A
public class Apple<T supers A>{}

1. Unbounded wildcards are <? >, For example, list <? >

The main function of unbounded wildcards is to enable generic types to accept unknown types of data

2. Upper bounded wildcards, using <? Forms of extensions E >

Using a generic type with a fixed upper bound wildcard, you can accept the data of the specified class and its subclass types.

To declare the use of this kind of wildcard, use <? The form of extensions E > where e is the upper boundary of the generic.

Note: Although the extends keyword is used here, it is not limited to the subclasses that inherit the parent class E, but also refers to the classes that show the interface E

3. Lower bounded wildcards with <? Form of super E >

Using the generic type of wildcard with fixed lower boundary, you can accept the data of the specified class and its parent class type.

To declare the use of this kind of wildcard, use <? The form of super E > where e is the lower boundary of the generic.

Note: you can specify an upper or lower boundary for a generic, but you cannot specify both.

5: The meaning of KTVE in generics

If you click the source code of some generic classes in JDK, we will see the following codes:

public class ArrayList<E> extends AbstractList<E> implements List<E>, RandomAccess, Cloneable, java.io.Serializable{
...
}
public class HashMap<K,V> extends AbstractMap<K,V> implements Map<K,V>, Cloneable, Serializable {
...
}

What do the generic parameters e, K, and V in the generic class definitions above mean? In fact, these parameter names can be specified arbitrarily, just like the parameter names of methods, but we usually give a meaningful name to let others know what it means at a glance. Generic parameters are the same. E generally refers to elements used in collection classes.

Common generic parameter names are as follows:

E: Element (used in a collection because the collection holds elements)
T: Type (Java class)
K: Key
5: V alue
N: Number (numeric type)
?: Represents an ambiguous java type

6: Implementation principle of generics

The essence of generics is to parameterize data types, which is realized by erasing, that is, the compiler will "erase" the generic syntax during compilation and make some type conversion actions accordingly.

It should be clear from an example, for example:

public class Caculate<T> {
private T num;
}

We define a generic class and an attribute member. The type of the member is a generic type. We don'T know what type this T is. It is only used to limit the type.

Decompile this compute class:

public class Caculate{
public Caculate(){}
private Object num;
}

It was found that the compiler erased the two angle brackets after the calculate class and defined the type of num as Object type.

So are all generic types erased with Object? In most cases, generic types are replaced with Object, but in one case, they are not. That is, the bounded types using the extends and super syntax, such as:

public class Caculate<T extends String> {
private T num;
}

In this case, num will be replaced with String instead of Object.

This is a type qualified syntax. It restricts T to be a String or a subclass of String, that is, when you build a compute instance, you can only restrict T to be a subclass of String or String. Therefore, no matter what type you limit T, String is the parent class and there will be no type mismatch. Therefore, you can use String for type erasure.

In fact, the compiler will normally compile where generics are used, erase types, and then return instances. However, in addition, if generic syntax is used when building a generic instance, the compiler will mark the instance and pay attention to all subsequent method calls of the instance. Before each call, the security check is carried out, and methods of non specified types cannot be called successfully.

In fact, the compiler not only pays attention to the call of a generic method, but also performs forced type conversion for some methods whose return value is a limited generic type. Due to type erasure, all methods whose return value is a generic type will be erased into Object type. When these methods are called, the compiler will insert an additional line of checkcast instruction for forced type conversion, This process is called "generic translation".

7: Finally

Above, I have made a complete and detailed explanation from the birth of Java generics, the use of generics, and the implementation principle of generics. I hope it will be useful to you!

About the author: mikechen , more than ten years of BAT architecture experience, senior technical expert, once worked in Alibaba, Taobao and Baidu.

Welcome to the official account No. mikechen's Internet Architecture , more than ten years of BAT architecture experience!

In the official account menu bar dialog box, reply to the key words of the structure, you can see my original 300 phase +BAT architecture technology series and 1000+ big factory interview questions answer set.

Topics: Java Back-end