Detailed explanation of Java generics, the most complete graphic and textual explanation in history

Generics have a very important position in java, whether it is an open source framework or JDK source code, it can be seen.

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

First, the nature of generic

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

The essence of generics is to parameterize types , that is, to specify a parameter for the type, and then specify the specific value of this parameter when using it, so that the type can be determined when it is used. 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 benefit of generics is that type safety is checked at compile time, and all casts are automatic and implicit, improving code reuse.

(1) Type safety is guaranteed.

Before generics, every object read from a collection had to be converted, and if you accidentally inserted an object of the wrong type, the conversion process would go wrong at runtime.

For example: using collections without generics:

public static void noGeneric() {

ArrayList names = new ArrayList();

names.add("mikechen的互联网架构");

names.add(123); //编译正常

}

 Use collections with generics:

public static void useGeneric() {

ArrayList<String> names = new ArrayList<>();

names.add("mikechen的互联网架构");

names.add(123); //编译不通过

}

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

It is equivalent to telling the compiler what type of objects each collection receives. The compiler will perform type checking at compile time to inform whether objects of the wrong type are inserted, making the program safer and enhancing the robustness of the program.

(2) Eliminate forced conversion

A side benefit of generics is that it eliminates many casts in the source code, which makes the code more readable and reduces the chance of errors.

As an example, the following code snippet without generics requires casting:

List list = new ArrayList();

list.add("hello");

String s = (String) list.get(0);

 When rewritten 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) Avoid unnecessary packing and unpacking operations and improve the performance of the program

In non-generic programming, passing a single type as Object causes Boxing and Unboxing operations, both of which are expensive. After the introduction of generics, there is no need to perform Boxing and Unboxing operations, so the operating efficiency is relatively high, especially in systems with frequent collection operations, the performance improvement brought by this feature is more obvious.

Generic variables have a fixed type, and when they are used, they already know whether it is a value type or a reference type, avoiding unnecessary boxing and unboxing operations.

object a=1;//由于是object类型,会自动进行装箱操作。

int b=(int)a;//强制转换,拆箱操作。这样一去一来,当次数多了以后会影响程序的运行效率。

After using generics

public static T GetValue<T>(T a)

{
  return a;
}

public static void Main()

{
  int b=GetValue<int>(1);//使用这个方法的时候已经指定了类型是int,所以不会有装箱和拆箱的操作。
}

(4) Improve the reusability of the code.

Three: How to use generics

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

1. Generic class

Generic class: define generics on a class

Definition format:

public class 类名 <泛型类型1,...> {  

}

Note: Generic types must be reference types (non-primitive 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, and multiple parameters are separated by commas:

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

Of course, the latter parameter type is also standardized and cannot be as arbitrary as above. Usually, we use uppercase single letters for type parameters:

T:任意类型 type
E:集合中元素的类型 element
K:key-value形式 key
V:key-value形式 value
示例代码:

 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:泛型类

GenericClass<String> name = new GenericClass<>("mikechen的互联网架构");

System.out.println(name.getValue());

GenericClass<Integer> number = new GenericClass<>(123);

System.out.println(number.getValue());

operation result:

Overview of generic methods: defining generics on methods

Definition format:

public <泛型类型> 返回类型 方法名(泛型类型 变量名) {
  
}
  • Points to note:

    • The formal parameters defined in the method declaration can only be used in the method, while the type parameters defined in the interface and class declaration can be used in the whole interface and class. When calling the fun() method, according to the actual object passed in, the compiler will determine the actual type represented by the type parameter T.

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 generics, the generic types defined before and after must be consistent, otherwise a compilation exception will occur:

GenericInterface<String> genericInterface = new NumberShowImpl();//编译异常

Or simply don't specify the type, then any type of new is fine:

GenericInterface g1 = new NumberShowImpl();

GenericInterface g2 = new StringShowImpl();

3. Generic method

Generic method is to specify the specific type of the generic when calling the method.

  • Definition format:

modifier <variable representing generic type> return value type method name (parameter) { }

E.g:

/**

     *

     * @param t 传入泛型的参数

     * @param <T> 泛型的类型

     * @return T 返回值为T类型

     * 说明:

     *   1)public 与 返回值中间<T>非常重要,可以理解为声明此方法为泛型方法。

     *   2)只有声明了<T>的方法才是泛型方法,泛型类中的使用了泛型的成员方法并不是泛型方法。

     *   3)<T>表明该方法将使用泛型类型T,此时才可以在方法中使用泛型类型T。

     *   4)与泛型类的定义一样,此处T可以随便写为任意标识,常见的如T、E等形式的参数常用于表示泛型。

     */

    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"); //这里的泛型跟下面调用的泛型方法可以不一样。

    String str = genericString.genercMethod("hello");//传入的是String类型,返回的也是String类型

    Integer i = genericString.genercMethod(123);//传入的是Integer类型,返回的也是Integer类型

}
 

class java.lang.String

hello
 
class java.lang.Integer

123

It can be seen here that the generic method gets different types as our incoming parameter types are different. Generic methods enable methods to vary independently of the class.

Four: Generic wildcards

The wildcard of Java generics is a special syntax used to solve the problem of passing by reference between generics, mainly in the following three categories:

// 1:表示类型参数可以是任何类型

public class Apple<?>{} 

// 2:表示类型参数必须是A或者是A的子类

public class Apple<T extends A>{} 

// 3: 表示类型参数必须是A或者是A的超类型

public class Apple<T supers A>{}

1. Unbounded Wildcards, which is <?>, such as List<?>
The main function of unbounded wildcards is to allow generics to accept data of unknown types.

2. Upper bounded wildcards (Upper Bounded Wildcards), in the form of <? extends E>

Using wildcard generics with fixed upper bounds, you can accept data of the specified class and its subclass types.

To declare the use of this class of wildcards, use the form of <? extends E>, where E is the upper bound of the generic type.

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

3. Lower bounded wildcards (Lower Bounded Wildcards), in the form of <? super E>

Wildcard generics with a fixed lower bound can accept data of the specified class and its superclass types.

To declare the use of this class of wildcards, use the form of <? super E>, where E is the lower bound of the generic type.

Note: You can specify upper or lower bounds for a generic, but not both.

Five: The meaning of KTVE in generics

If we click on the source code of some generic classes in the 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 above generic class definitions 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.

Common generic parameter names are as follows:

E: Element (used in collections, because collections store elements)
T: Type (Java class)
K: Key (key)
V: Value (value)
N: Number (numeric type)
? : Represents an indeterminate java type

Six: the realization principle of generics

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

It should be clear to look at an example, such as:​​​​​​​

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

We define a generic class, define an attribute member, the type of the member is a generic type, we don't know what type of T is, it is only used to qualify the type.

Decompile this Caculate class:​​​​​​​

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

It was found that the compiler erased the two angle brackets after the Caculate class and defined the type of num to be of type Object.

So are all generic types erased with Object? Generic types are replaced with Object in most cases, but not in one case. That is a bounded type that uses the extends and super syntax, such as:​​​​​​​

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

For generic types in this case, num is replaced by String instead of Object.

This is a type-qualified syntax, which restricts T to be a subclass of String or String, that is, when you construct a Caculate instance, you can only restrict T to be a subclass of String or String, so no matter what type of T you restrict, String is The parent class, there will be no type mismatch problem, so String can be used for type erasure.

In fact, the compiler will normally compile and type erasure where generics are used, and then return the instance. But in addition to this, if the 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, and perform security checks before each call. method cannot be called successfully.

In fact, the compiler not only pays attention to the invocation of a generic method, but it also performs type conversion for some methods whose return value is a qualified generic type. Due to type erasure, methods whose return value is a generic type will be erased. When these methods are called, the compiler will insert an extra line of checkcast instruction for type conversion, this process is called "generic translation".

Seven: the last

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

Guess you like

Origin blog.csdn.net/qq_41701956/article/details/123473592