“This is the second day of my participation in the November Gwen Challenge. See details of the event: The last Gwen Challenge 2021”.

The generic

Generics are a type of markup introduced in the JAVA world in JDK 5.0 that can be used in classes, interfaces, or methods.

Why generics?

In fact, the analogy between non-relational databases and relational databases,

  • Non-relational databases are simple to implement and efficient to access, but anyone who has used them knows how uncomfortable they can be when used for complex logic.
  • The schema design of relational databases allows data to be just columns.
  • C and JDK native arrays also have specific types
  • Java says that everything is an object, that an object is an instance of a class, but container types like HashMap and ArrayList are designed without the specific type layer in mind, resulting in poor management.

So the design before JDK 5.0 was a bit of a problem. Maybe arrays were thought to be enough, but various container classes like HashMap and ArrayList were designed.

In this case, it is not clear what type is inside the container, for example, it is always easy to make errors when doing type conversion and calculation, resulting in the program is easy to crash.

When God created the world, it was not perfect at the beginning. There was also a bug in the world of classes and objects, which made the code always prone to errors.

So the generic design was introduced in JDK 5.0.

What are generics?

A symbol introduced in the JAVA world after JDK 5.0 to mark element types that can be used in classes, interfaces, or methods. The element types here include the types of members, function return values, and method parameters.

So how do you use them?

A generic class

class MyList<T>{
    private T[] elements;
    private int size;
    public void add(T item) {
        elements[size++]=item;
    }
    public Object get(int index) {
        returnelements[index]; }}Copy the code

As can be seen:

  • MyList

    : T denotes the type of the member of the class
  • The symbol T is used for the member elements

A generic interface

Interfaces are used similarly to classes

// Define a generic interface
public interface Gen<T> {
    public T func(a);
}

// The implementation is still generic class
class GenImpl1<T> implements Gen<T> {
    @Override
    public T func(a) {
        return null; }}// The implemented class is no longer a generic class, so the type of T needs to be written.
class GenImpl2 implements Gen<String> {
    @Override
    public String func(a) {
        return null; }}Copy the code

The demo code above:

  • Defines a generic interface Gen
  • T is the return type of the func function

We know that interfaces are implemented by classes, but how do other classes implement interfaces with generics?

There are two ways:

  • The implemented class itself could still beA generic classJust like hereclass GenImpl1<T> implements Gen<T>
  • Implementation of the classNo longer a generic classThen declare the interfacetagsymbolTwithSpecific type to replaceJust like hereclass GenImpl2 implements Gen<String>

Generic method

Methods can also support generics.

class GenericFunc {
    / * * * *@param e
     * @param <E>
     */
    public <E> void test(E e) { System.out.println(e); }}Copy the code
  • callnew GenericFunc().test("hello");returnhello
  • Generic classes place the generic token after the class name
  • And the way to do that is to putMethod before the return value, such as <E> void test(E e)
  • This is the type that defines the method parameters. It’s generic E, so you can type in any type of parameter and compile it without any problem. Okay

Support for generic JDK

Before JDK 5.0, generics were not supported. In the virtual machine world, there were only classes and objects.

System.out.println("class: "+ HashMap.class);
System.out.println("class: "+ HashMap<String,String>.class);
Copy the code

Hashmap. class exists, but the compiler will fail to write HashMap

. Class.
,string>

In fact, after 5.0, this will be an error. In the virtual machine world, there are still only classes and objects. In other words:

  • The RUNTIME environment of the JDK is essentially unchanged, with no support for generics.
  • JDK 5.0 and later compilers support generics

The compiler supports generics

JDK 5.0 and laterThe compilerHow do you support generics? The compiler does what is shown below.

  • First, the compiler checks the code’s generic declaration and usage during compilation
class THolder<T> {
    T item;
    public void setData(T t) {
        this.item = t;
    }

    public T getData(a) {
        return this.item;
    }

    public static void main(String[] args) {
        THolder<Integer> holder = new THolder<>();
        holder.setData("abc"); holder.getData(); }}Copy the code

Here setData is “ABC”, type is not Integer, will not compile.

  • The compiler then removes it during compilationparametertheMark symbolInformation, and use a TSpecific type substitution, usuallyObject.
    • This process (replacing with an Object class instead of a specified declared class) is called erasing

Do the following:

 # javac THolder.java
 # javap -verbose THolder.class
Copy the code

To get the decompiled bytecode:

{ T item; descriptor: Ljava/lang/Object; .public void setData(T); descriptor: (Ljava/lang/Object;) V flags: ACC_PUBLIC Code: stack=2, locals=2, args_size=2
         0: aload_0
         1: aload_1
         2: putfield      #2                  // Field item:Ljava/lang/Object;
         5: return.public T getData(a);
    descriptor: ()Ljava/lang/Object;
    flags: ACC_PUBLIC
    Code:
      stack=1, locals=1, args_size=1
         0: aload_0
         1: getfield      #2                  // Field item:Ljava/lang/Object;
         4: areturn
  ...
 public static void main(java.lang.String[]);
    descriptor: ([Ljava/lang/String;)V
    flags: ACC_PUBLIC, ACC_STATIC
    Code:
      stack=2, locals=3, args_size=1
         0: new           #3                  // class THolder.20: checkcast     #8                  // class java/lang/Integer
        23: astore_2
        24: return
}
Copy the code

It can be seen that

  • The data type of the putfield instruction in the setData function is Object
  • The gettfield instruction in the getData function has a data type of Object
  • Finally, a type cast is done
    • The checkcast instruction at getData in main casts the Object type to Java /lang/Integer

Can I not erase it?

Why not just use Integer instead of Object for generic erasure?

In fact, it is possible not to erase, and there is no checkcast step.

The reason the JDK does this is because of historical code compatibility.

How can I put it? Bytecodes compiled prior to JDK 5.0 (2004) look like this: obj1:

Now, if the type is not erased, the container object obj2 looks like this:

So now obj1=obj2 is not an assignment. But if you erase it, you can, because the type is Object. So now, if you don’t erase the type, it’s not going to be consistent with the semantics of the old ArrayList.

So the HotSpot virtual machine uses an erasure mechanism to maintain this historical consistency.

Of course, some other virtual machines may not use the erase mechanism, of course, more efficient.

Other points to note

  • Because generics do not support some runtime features (and were eventually erased), be aware that some scripts will compile but, for example, new

  • The upper bound
    : This is a way to add a boundary to a parameter type

class THolder<T extends Parent> {
    T item;
    public void setData(T t) {
        this.item = t;
    }
    public T getData(a) {
        return this.item;
    }
    public static void main(String[] args) {
        THolder<Child> holder = new THolder<>();
        holder.setData(newChild()); }}interface Parent{...class Child implements Parent{...Copy the code

If the code is repeated as compiled and decompiled above, the bytecode looks like this:

         2: putfield      #2                  // Field item:LParent;
         1: getfield      #2                  // Field item:LParent;   
Copy the code

You can see that the type used here is Parent instead of Object.

  • <? Super T> : this is used to add a lower boundary to the parameter type, analogous<? extends T>
  • : There is no limit

Spring supports generics

The bean injection types in Spring 4.0 also support generics

conclusion

In fact, Java generics are designed to solve the defects of JDK container classes, and the introduction of generics is a perfect solution to the problem of type confusion.

And then icing on the cake, making all the classes more generic, a little bit more container-like.