This is the third day of my participation in the August More text Challenge. For details, see: August More Text Challenge

📑 Soon to learn

Generics

background

Let’s look at these two pieces of code first

public int addInt(int x,int y){
    return x + y;
}
public float addFloat(float x, float y){
    return x + y;
}
Copy the code
List list= new ArrayList<String>();
list.add("123");
list.add(123);
list.add(123f);

for (int i = 0; i < list.size(); i++) {
    String value = ((String) list.get(i));
    System.out.println(value);
}
Copy the code

In the above two pieces of code, we can see some of the problems that arise in this situation.

Multiple data operations of the same data type

When the List stores an Object, you can pass the class and subclasses of that class. The compiled type of the Object will be Object in the type code, but it will still run as its own type, String, Int. Therefore, it’s easy to get cast exceptions when fetching elements from a list requires an artificial cast to the target type.

To solve this problem in lists, different instances of this type may have different types, and you need to restrict the collection class type, as well as cast some data. The conceptual solution of generics is proposed

Generic writing

Generic class writing

public class Wrapper<T> {

     T instance ;
    // This is not a generic method
    public T getInstance(a) {
        return instance;
    }

    public void setInstance(T instance) {
        this.instance = instance; }}Copy the code

Generic method writing

/ / declare
<E> E method(E item);
/ / useString newStr = Related class.<String>method("method"); reference@SuppressWarnings("TypeParameterUnusedInFormals")
    @Override
    public <T extends View> T findViewById(@IdRes int id) {
        return getDelegate().findViewById(id);
    }
Copy the code

Generic advantage

  • Type checking automatically transitions
  • Type constraints

This allows multiple data types to perform the same method, as well as runtime instantiation of generic parameters and automatic transformation. Although we can do this in Java, generics make the process much easier and faster

class GenericList<T> {
    public Object[] instances = new Object[0];

    public T get(int index) {
        return (T) instances[index];
    }

    public void set(int index,T instance){
        instances[index] = instance;
    }

    public void add(T instance){
        instances = Arrays.copyOf(instances,instances.length + 1);
        instances[instances.length -1] = instance;
    }

}


GenericList<String> stringGenericList = new GenericList<>();
        stringGenericList.add("asd");
        // An error is automatically reported when a variable of non-generic instance type String is passed in
        //stringGenericList.add(123);
        // When fetching data from a generic type, it is automatically converted to a generic instantiation type
        String s = stringGenericList.get(0);
Copy the code

We can see that the use of generics allows us to restrict the type and to strong-twist some data but we can do that without generics

class NonGenericList {
    public Object[] instances = new Object[0];

    public Object get(int index) {
        return instances[index];
    }

    public void set(int index,Object instance){
        instances[index] = instance;
    }
 
    public void add(Object instance){
        instances = Arrays.copyOf(instances,instances.length + 1);
        instances[instances.length -1] = instance; }}Copy the code

We can use Object to achieve arbitrary storage, and then judge to achieve specific type of storage, and finally use cast when using can still achieve this effect


        NonGenericList nonGenericList = new NonGenericList();

        if ("community" instanceof String){
            nonGenericList.add("community");
        }
        
        String result = (String) nonGenericList.get(0);
Copy the code

But generics make it much faster

Generic application scenarios

The return types of parameter types of methods of certain fields of a class or interface are variable

Instance types are not static parameters for static types

< T > in generics

//RepairableShop
      
        is a declaration that Shop
       
         is instantiated
       
//RepairableShop
      
        indicates that I have a generic parameter M
      
//shop
      
        is instantiated. M is an instantiation of the generic type shop
      
public interface RepairableShop<T> extends Shop<T> {
    /** * The interface that wants to add functionality to the previous generic interface and keep the generic * type parameter T inherits the Shop interface and the Shop interface parameter is T instantiates the Shop parameter T * with the RepairableShop type parameter T and the Shop parameter is T during instantiation * E to the left is the declaration of the Repairable type parameter; The E on the right is an instantiation of Shop's type parameter */
}
Copy the code

Constraints and limitations of generics

Type variable cannot be instantiated

Type parameter ‘T’ cannot be instantiated directly

 / / no
 public T get(int index) {
        return new T();
        
 }
Copy the code

A generic type parameter cannot be instantiated with a basic data type

GenericList<Integer> integerGenericList = new GenericList<>();/ / line
GenericList<int> intGenericList = new GenericList<>();/ / no
Copy the code

The static context type variable of a generic class is invalid

    // Since generics are instance specific, static types are not acceptable
    private staticT instacne; / wrongpublic static <T> T getInstacne(a){
        // This is also problematic
    };
Copy the code

Cannot create an array of parameterized types

List<String>[] strings = new List<String>[100]; / / noCopy the code

Generic types instantiate upper and lower bounds

whyArrayList<Coffee> coffees = new ArrayList<Latte>();complains

Coffee is the parent class of Latte, so what they’re saying abstractively is I’m declaring a container for Coffee, any Coffee I want and I’m instantiating a container for Latte and assigning it to the declared variable. So, when we get the data, we’re going to get an instantiated object of the coffee interface or subclass, and we’re going to fail to convert the object at this point, I want a container that can hold any coffee, you give me a container that can and can only hold lattes.

But in our actual development, it’s a very common requirement that we declare a coffee cup to be a coffee cup.

The wildcard?

Write only where the generic type is instantiated

It means that the type is whatever it is, as long as it doesn’t exceed, right? The extend or? Limit of super. Although used for instantiation, it indicates that the type has yet to be determined. It cannot be used when new Goods
();

ArrayList

coffees = new ArrayList

(); We’re going to get an error here when we use, right? After the wildcard, we’ll notice that the compiler has stopped reporting errors

// Create an upper bound for the class
public interface CoffeeShop<T.C extends Coffee> extends Shop<T>{}// Declare the upper bound of the class
ArrayList<? extends Coffee> coffees = new ArrayList<Latte>();
Copy the code

At this point, it’s an upper bound, it’s a promise, even though this line is not going to make an error, we can’t pass in the wrong type.

A generic type that cannot be subclassed is assigned to a generic reference of its parent class

? The argument passed to the method must be a subclass of X including X itself

You can relax the declaration requirements by declaring subclasses

At this time we can need a coffee cup, pass any coffee cup

? The extend restrictions

However, in our case above, we cannot call a method passing generic parameters that contains some set or other methods because the compiler does not know the actual parameters at run time and may pass in subclass type parameters, resulting in an error. The advantage of this limitation is that you can access X and its subtypes primarily for secure data access

In the case above we used wildcards to solve the case where generic subclasses pass references to the superclass so? Why only generics have this situation and not the following code

You give me an apple. Apples are fruits
Fruit fruit = new Apple();//1 The compiler does not report an error
// Replace full Apple with full Peach
fruit = new Peach();//2 The compiler does not report an error
// I need a fruit container. You gave me an apple-only container. Peach inserted behind the apple-only container
ArrayList<Fruit> fruits = new ArrayList<Apple>();//3 The compiler reports an error
List<Fruit> fruits = new ArrayList<Fruit>();//4 The compiler does not report an error


Fruit[] fruits = new Apple[10];// The compiler does not report an error
// Error Peach Cannot be stored in Array type of Apple[]
fruits[0] = new Peach();// The compiler does not report an error, but this line of code is problematic and will report an error at runtime


// The compiler does not report an error, but this line of code is problematic, but it does not report an error at runtime
// Because generics have a type erasure feature, their generics will be erased at runtime
*ArrayList coffeeArrayList = (ArrayList) new ArrayList(); *public interface Shop 
      
        { * T sell(); * float refund(T item); Public interface Shop{* Object sell(); * float refund(Object item); *} * * /
      
 ArrayList<Coffee> coffeeArrayList = (ArrayList) new ArrayList<Cappuccino>();
 coffeeArrayList.add(new Latte());
 
 
 ArrayList<Cappuccino> cappuccinoArrayList = (ArrayList) new ArrayList<Cappuccino>();
 ArrayList<Coffee> coffeeArrayList = cappuccinoArrayList;
 coffeeArrayList.add(new Latte());
 Cannot assign type objects of subclass to type references of superclass because of Java type erasures
 // Arrays have no type erasures, which can be detected at run time, whereas generics can't be used when an exception occurs
 // There is such a mechanism
 Cappuccino cappuccino = cappuccinoArrayList.get(0);
 
Copy the code

? Extends the use of

Used primarily to securely access data, Coffee and its subtypes can be accessed primarily for contextualized uses Contextualized uses: In some methods, subclasses passed to the parent class do not restrict the receiving type of the function

 Shop<? extends Coffee> coffeeShop = new Shop<Latte>(){};
 
 // We don't usually use this. We don't usually create fields
 ArrayList<? extends Coffee> coffeeLists = new ArrayList<Cappuccino>();
 float totalSugar = 0;
 for(Coffee coffer:coffeeLists){
     totalSugar += coffer.getSugr();
 }
 
 // This is mainly used in scenarios where the requirement has a specific requirement declaration for others to use
 // This is primarily used to get secure access to data by methods that do not contain generic parameters
 float getTotalSugar(ArrayList<? extends Coffee> coffeeLists){
    float totalSugar = 0;
	for(Coffee coffer:coffeeLists){
    	totalSugar += coffer.getSugr();
 	}
 	return totalSugar;
}
Copy the code

? super X

Represents the argument passed to the method, which must be a superclass of X (including X itself)

? Super X represents the lower bound of the type, and the type argument is a superclass of X (including X itself). Therefore, it is safe to say that the superclass returned by get must be a superclass of X. I don’t know, but I’m sure Object must be its superclass, so the get method returns Object. The compiler knows for sure. For the set method, the compiler does not know the exact type it needs, but X and subclasses of X can safely be converted to X.

public class Mocha implements Coffee{
	public void addMeToCoffeeList(ArrayList<Mocha> arrayList){        
		arrayList.add(this); }}// The above method we found
ArrayList<Cappuccino> cappuccinoArrayList = new ArrayList<Cappuccino>();
ArrayList<Coffee> coffeeLists = new ArrayList<Coffee>();

Mocha mocha = new Mocha();
mocha.addMeToCoffeeList(cappuccinoArrayList)/ / normal
mocha.addMeToCoffeeList(coffeeLists)// This is a normal coffee container with mocha as long as it can hold mocha


Copy the code

? Scenarios of Super XXX

Declare this variable whose method arguments are arguments of this type and the superclass generics can certainly accept this type

// Method modification
public class Mocha implements Coffee{
	public void addMeToCoffeeList(ArrayList<? super Mocha> arrayList){        
		arrayList.add(this); }}Copy the code

Generic method

Generic methods themselves declare methods with generic parameters

<O> List<T> recycle(O item);
Copy the code

Why use generic methods

Scenario: When we have a class that needs to introduce new types of data, such as the store interface

public interface Shop <T> {
    T sell(a);
    float refund(T item);
}
// We want to add a recycling method that can recycle any number of items and then give them back to me when I sell them
// At this point, we can add new methods according to the requirements
list<T> recycle(G goods);
// Since the good is any kind of good, we add the generic G to the method
// Since G is a newly defined type, we can first define the generic type G in the interfaceThe code structure ispublic interface Shop <T.G> {
    T sell(a);
    float refund(T item);
    list<T> recycle(G goods);
}
// We need to pass in the relevant type when we need to use it

Shop shop = newShop<Television Television >(){} At this point we find that we need to pass in the relevant class when creating the store. It has violated the we want to achieve recycling any item needs In order to solve this demand So we thought about the interface well To build a home appliance interface Related item implement this interface But in order to so is not restricted Why don't we let the incoming parameters directly to the Object, at this time We can directly at the time of usepublic interface Shop <T> {
    T sell(a);
    float refund(T item);
    list<T> recycle(Object goods); } We can pass in the related instance directly without using the interface. It's faster to pass in the related instance object directly without passing in the generic type. If it's not a generic can also meet the requirements (it does not have to type checking and automatic transformation) Can not use generics And when we are in a kind of demand Now need to old change new we need to approach the incoming goods and some of the money And then return any new items We can according to demand design method Because it is in any item Just like we did with any of the goods we can use the above method to return Object and then Object tradeIn (Object Goods,floatMoney); When we use Object Goods = shop.tradein (new Television(),1000); Television tv = (Television)goods; <G>G tradeIn (G goods, G goods)floatMoney); We can see what type is returned by inferring the type of the class we passed in without transitioning. Television goods = shop.tradeIn(new Television(),1000); This G is not for this class but for this methodCopy the code

Instantiation of generic parameters

Creation of the Shop<Coffee> objectnewArrayList<String>() inherits a call to a generic methodCopy the code

Rely on generic methods to transform automatically

<R> R take();

We can do this through shop.take(); To infer that the return value type parameter is Mocha

The return value parameter Mocha Mocha = shop.take(); can also be inferred in this way.

Analogously to findViewById passing in resId, deduce the control type from the previous type

 @SuppressWarnings("TypeParameterUnusedInFormals")
    @Override
    public <T extends View> T findViewById(@IdRes int id) {
        return getDelegate().findViewById(id);
    }
Copy the code

Instantiate the generic parameter on each call

Back to why use generic methods

<G>list<T> recycle(G goods);
list<T> recycle(Object goods); Comparing these two lines of code, we can see that the generics of the store are passed in as arguments and there is only one return value of the generics parameter. In this design process, we can observe that generics do not work and are not necessaryCopy the code

Generic methods are independent of the current object itself and are not limited to non-static methods, so it is possible to make a static method a generic method that is dependent on each call, and each call instantiates a generic parameter.

The nature of generics

Type checking and automatic transformation (surface)

When does nature want type checking and automatic transformation

The lock type can be instantiated for multiple different implementations and later locked each time the lock is used

Class String Implement Comparable<String>{} String is not related to Comparable<String> String implements the Comparable interface and instantiates the generic parameter to StringCopy the code

Type constraints Generics can have multiple restrictions

interface AppleShop<T extends Apple & Serializable>{
	T buy(a);
	
	float refund(T item);
}
Copy the code

Use generics to qualify method parameter types to return value types

<p> void merge(List<p> list1, List<p> lisr2)
Copy the code

Usage situation induction

T The right side of the class name and the right side of the interface name are just type parameters

Type parameters have two analogies to method parameters

Parameter specifies when a method parameter is declared

The instance passed in when the argument to the argument method is actually used

  • type parameter

    • Create public class Shop;

      • Create a Shop class that uses the same type internally, called T
  • type argument

    • Elsewhere, Shop, appleShop, Apple in Angle brackets;

    • That’s the same type, and in this case we decided it’s the type like Apple.

interface RepairableShop<T> extends Shop<T>{
	// I want to instantiate the parent class to determine the actual value of its type parameter
	// The specific type to instantiate is my type parameter
}

Copy the code

A generic is only meaningful when it’s declared inside the class definition and once it’s outside the class itself, you’re using the class

?

Expand the scope of arguments when instantiated extends to create the upper bound of the generic type

<>

Takes a parameter to a type to wrap a delimiter around the type

Generics repetition and nesting

public abstract class Enum<E extends Enum<E>> 
	implements Comparable<E>{
	
	// Duplicate and nested
	
	// Extends Enum
      
        Indicates that the upper bound E requires a subclass of Enum
       
	// E is a subclass of Enum or itself when instantiated
	An implementation of Comparable
      
        needs to override comparaTo (E o) by having an argument that is a subclass of Enum
       
         to indicate that it must be compared to its own class
       E is a subclass or itself of Enum when instantiated.Copy the code

Type erasure

Take a look at the following two pieces of code

So you can see that the compiler is reporting an error and here’s the error message

‘method (List)’ conflicts with’ method (List)’; The two methods have the same styles of ‘method(List)’ clashes with ‘method(List)’; both methods have same erasure

In Java, by type erasing List and List both become List so these two methods are actually the same at run time so the compiler makes an error

Generics in the Java language are different. They exist only in the source code of the program. In the compiled bytecode file, the original Raw Type has been replaced and the forced transformation code has been inserted in place. ArrayList < int > is the same class as ArrayList < String >, so generics technology is actually a syntactic sugar of the Java language. The implementation method of generics in the Java language is called type erasing, and the generics implemented based on this method are called pseudo-generics.

The virtual machine is in the generics section. Signature and so on are introduced to save parameter types and parameterized type information, and some types are transformed by reflection to use generics

And within that, there’s a bridge method

//@Override
float refund(Apple item);// We rewrote it

// What the JVM added is essentially this overwrite
@Override
float method(Object item);
Copy the code

For float method(Object item); The method JVM does the following

@Override
float method(Object item){
	return refund((Apple)item);
}
Copy the code

Effect of type erasure

List.class cannot be obtained because of type erasing

To eliminate impact

From the Signature attribute, erasure refers to the erasure of bytecode in the Code attribute of the method. In fact, the generic information remains in the metadata. We can obtain parameterized types by reflection.

So type erasing is just a way of seeing the type information at runtime without it being in the bytecode

All variables, parameters, classes, and interfaces declared in code can be obtained at run time through reflection

So we can use reflection technology to get the type information

However, when an object is created at run time, the generic information of the object is not obtained by reflection (the Class file does not) to generate the object at run time, so since the subclass is in the Class file, the generic information of the object created at run time can be obtained by reflection

This is how frameworks such as GSon handle generic information