Thank you for joining me in the Android world to fight monsters and upgrade!

Generics, a commonplace feature in the JAVA world that everyone knows how to use. More need to know, know why.

What are generics

Generics are a parameterized type feature introduced in JDK1.5 that allows multiple data types to be manipulated on the same piece of code.

1.1 Parameterized Types

Let’s take the use of generic classes as an example:

// The generic class definition
public class Generics<T> {
    // Unknown type
    private T mData;

    public T getData(a) {
        return mData;
    }

    public void setData(T data) {
        this.mData = data; }}Copy the code

Generic [T] is defined within a generic class, where [T] is an unknown type.

// The use of generic classes, passing the Person class as an argument to the generic class
Generics<Person> generics = new Generics<Person>();
Copy the code

When the generic class creates an object, we pass the Person class as a parameter to the generic class, at which point the [T] inside the generic class becomes the known type Person.

The type passed in as a parameter, as a generic type, is a parameterized type.

Generic types and boundaries

2.1 Generic types

1. Generic interfaces

public interface Base<T> {

    public T getData(a);

    public void setData(T data);
    
}
Copy the code

2. A generic class

public class Generics<T>{
    private T mData;

    public T getData(a) {
        return mData;
    }

    public void setData(T data) {
        this.mData = data; }}Copy the code

3. Generic methods

// The 
      
        after public is the key to generic methods
      
public <T> Generics<T> getGenerics(a) {
    return new Generics<T>();
}
Copy the code

2.2 Generic boundaries

All of the above types define the boundaries of generics, syntax, <T extends A&B… >, generics override the extends keyword, unlike extends normally used in JAVA.

  • < T extends A> : A single boundary. A can be A class or interface and can only accept types that inherit or implement A.
  • < T extends A&B&… > < span style = “box-sizing: border-box; color: RGB (74, 74, 74); For example, in

    , T must inherit type A or implement interfaces A, and it must implement interfaces B and C.

The benefits of generics

3.1 Code is more robust

Generics advance collection type detection to compile time, ensuring that errors are thrown at compile time. Basically, code editors (Android Studio, IDEA, etc.) pass error types to generics during code writing.

Until you have generics, you can only throw a ClassCastException at runtime, and the code is very fragile.

// Before generics existed
"Fruit" and "Dog" are stored in "Fruit" and "Dog"
List fruits = new ArrayList();
fruits.add(new Fruit());
fruits.add(new Dog()); // X error insert until run error
Copy the code
// After generics exist
List<Fruit> fruits = new ArrayList<Fruit>();
fruits.add(new Fruit());
fruits.add(new Dog());// X gets an error when it compiles
Copy the code

3.2 More concise code

Generics eliminate casting of types. Before generics, objects in a collection were all cast upward to Object, so strong casting was required.

// Before generics, fetching objects required a strong twist
Fruit fruit = (Fruit) fruits.get(0);
Copy the code

3.3 Strong code reuse

Generics are the use of parameterized types to manipulate multiple data types on a single piece of code. For example, if several classes are treated logically the same, it is natural to think that the logical code should be written only once, so generics are born.

The principle of generics

Generics were only introduced in JDK1.5. For backward compatibility, generics are not supported by virtual machines, so JAVA erasures generics at compile time in addition to type checking, so that all generics become primitive in bytecode. Unlike C# generics, JAVA uses pseudo generics.

4.1 Generic erasure

When bytecode is generated at compile time, generic erasers are performed, so if we look at the generated bytecode file, we can clearly see that generic [T] is converted to Object.

/ / Java code
public class Generics<T> {
    private T mData;

    public T getData(a) {
        return mData;
    }

    public void setData(T data) {
        this.mData = data; }}Copy the code

Here is the bytecode generated by the Generics class

// class version 51.0 (51)
// access flags 0x21
// signature 
      
       Ljava/lang/Object;
      
// declaration: com/kproduce/androidstudy/test/Generics<T>
public class com/kproduce/androidstudy/test/Generics {

  // compiled from: Generics.java

  // access flags 0x2
  // signature TT;
  // declaration: T
  private Ljava/lang/Object; mData

  // access flags 0x1
  public <init>()V
   L0
    LINENUMBER 6 L0
    ALOAD 0
    INVOKESPECIAL java/lang/Object.<init> ()V
    RETURN
   L1
    LOCALVARIABLE this Lcom/kproduce/androidstudy/test/Generics; L0 L1 0
    // signature Lcom/kproduce/androidstudy/test/Generics
      
       ;
      ;>
    // declaration: com.kproduce.androidstudy.test.Generics<T>
    MAXSTACK = 1
    MAXLOCALS = 1

  // access flags 0x1
  // signature ()TT;
  // declaration: T getData()
  public getData(a)Ljava/lang/Object;
   L0
    LINENUMBER 10 L0
    ALOAD 0
    GETFIELD com/kproduce/androidstudy/test/Generics.mData : Ljava/lang/Object;
    ARETURN
   L1
    LOCALVARIABLE this Lcom/kproduce/androidstudy/test/Generics; L0 L1 0
    // signature Lcom/kproduce/androidstudy/test/Generics
      
       ;
      ;>
    // declaration: com.kproduce.androidstudy.test.Generics<T>
    MAXSTACK = 1
    MAXLOCALS = 1

  // access flags 0x1
  // signature (TT;) V
  // declaration: void setData(T)
  public setData(Ljava/lang/Object;)V
   L0
    LINENUMBER 14 L0
    ALOAD 0
    ALOAD 1
    PUTFIELD com/kproduce/androidstudy/test/Generics.mData : Ljava/lang/Object;
   L1
    LINENUMBER 15 L1
    RETURN
   L2
    LOCALVARIABLE this Lcom/kproduce/androidstudy/test/Generics; L0 L2 0
    // signature Lcom/kproduce/androidstudy/test/Generics
      
       ;
      ;>
    // declaration: com.kproduce.androidstudy.test.Generics<T>
    LOCALVARIABLE data Ljava/lang/Object; L0 L2 1
    // signature TT;
    // declaration: T
    MAXSTACK = 2
    MAXLOCALS = 2
}

Copy the code

There is still a generic [T] in it.Yes, you’re right! So why are generics still in remarks? This is the time to sayreflectionThe concept.

Reflection is all properties and methods that are known at run time for any class. For any object, you can call its methods and properties. Is the key to JAVA being seen as a dynamic language.

Since reflection knows all the methods and attributes, but generics are erased from the bytecode, JAVA secretly writes generics into the bytecode using remarks to ensure reflection works.

4.2 Generic erase principle

  • If the generic type is not qualified (), Object is used as the primitive type.
  • If there is A qualification (), use A as the primitive type.
  • If there are more than one qualification (

    ), the first boundary, A, is used as the primitive type.

Qualified wildcards for generics

Wildcards make the transition to generics more flexible.

  • means “upper bound wildcard”
  • means “lower bound wildcard”

5.1 Meaning of WildCard Characters

Arrays can be upcast:

Object[] nums = new Integer[2];
nums[0] = 1;
nums[1] = "string"; // Nums is an Interger array at runtime, so an error is reported
Copy the code

Here’s another example of generic transformation code that will report an error:

// Apple extends Fruit, but this transition results in an error
List<Fruit> fruits = new List<Apple>();
Copy the code

Therefore, the transformation of generics has nothing to do with Apple extends Fruit. Generics cannot be directly transformed upward like arrays, so the purpose of wildcards is to make the transformation of generics more flexible.

5.2 Wildcard Description

  • Upper bound wildcard:
    , Fruit is the top boundary, you can only get, you can’t add. (Detailed explanation in code remarks)

public static void main(String[] args) {
    List<GreenApple> greenApples = new ArrayList<>();
    List<Apple> apples = new ArrayList<>();
    List<Food> foods = new ArrayList<>();
    setData(greenApples);
    setData(apples);
    setData(foods); // Compilation error, outside the limits
}

public void setData(List<? extends Fruit> list){
    // Upper wildcard, can only get, cannot add
    // we can only get it because we can ensure that the specified object in the list can be converted upward to Fruit
    // [can't add] because you can't determine which subclass it is,
    // Banana may be set to List
      
       , so it cannot be set
      
    Fruit fruit = list.get(0);
}
Copy the code
  • Lower bound wildcard:
    , Fruit is the bottom boundary, you can only add, you can’t get. (Detailed explanation in code remarks)

public static void main(String[] args) {
    List<Food> foods = new ArrayList<>();
    List<Apple> apples = new ArrayList<>();
    setData(foods);
    setData(apples); // Compilation error, outside the limits
}

public void setData(List<? super Fruit> list){
    // Lower wildcard, can only add, cannot get
    // we can only add because we can make sure that the object specified by list must be the parent of Fruit,
    // The Fruit subclass must be able to go up to its parent, so add.
    // [can't get] Because the specified object has no fixed upper bound, we don't know which parent class it is, so we can't accurately get the transformation to a certain class.
    list.add(new Apple());
    list.add(new Banana());
}
Copy the code

conclusion

Finally, let’s summarize the knowledge of generics:

  1. Generics are a parameterized type feature introduced in JDK1.5.
  2. Generics include generic interfaces, generic classes, generic methods, and you can use set boundaries.
  3. Generics make code more robust (compile-time errors), concise (not strong), and reusable.
  4. Generics are pseudo-generics in JAVA. Generic types are not supported in the virtual machine. Generics are erased at compile time, but comments are left for reflection.
  5. Generics wildcards make transitions more flexible. Upper wildcards can only be get, not add. The lower wildcard can only be add, not get.

That concludes the introduction to generics, and hopefully you’ll have a better understanding of generics after reading this article. If my article can bring you a little bit of welfare, I will be happy enough.

See you next time!

The public,

Below is my wechat public account [Lao Kuang Dialect Android], all series of articles will be updated synchronously in the public account, welcome to follow.