JavaSE advanced programming summary and training-generic programming 2

JavaSE advanced programming summary and training-generic programming 2

Fourth, the realization principle of generics

4.1, overview principle

Since generics were only produced in JDK1.5, before that, Java generics used Object objects to accept all types of objects. But the JVM must maintain backward compatibility, that is, the lower version of the JDK must be able to run on the higher version of the JDK! Because of this compatibility, our generics have to become the same class file as the bytecode before JDK1.5 after compilation.
Therefore, the principle of generics is still related to Object~~

4.2, look at the example

Example 1: Common generic class and common generic function


/**
 * @author jiangzl
 */
public class MyGeneric7 {
    
    

    public static <T> void getTemplateValue(MyNumber<T> myNumber){
    
    
        System.out.println(myNumber.getValue());
    }

    public static void main(String[] args) {
    
    
        MyNumber<Double> doubleMyNumber = new MyNumber<>(3.14);
        getTemplateValue(doubleMyNumber);
        MyNumber<Integer> integerMyNumber = new MyNumber<>(520);
        getTemplateValue(integerMyNumber);
    }
}

class MyNumber<T>{
    
    
    private T value;

    public MyNumber(T value){
    
    
        this.value = value;
    }

    public T getValue(){
    
    
        return value;
    }
}
Take a look at the output of the example:

Insert picture description here

Look at the results of the disassembly:
First: the result of MyNumber.class

Insert picture description here

The key point, please see: Field value

It is java/lang/Object;! ! ! This shows that after disassembly, this value becomes an Object of Object. Then the construction method is also Object. The invokespecial is simply to say that it means a specific function, not a virtual function~~

Second: the result of MyGeneric7.class

Insert picture description here

Seeing the parameters of the generic method, after disassembly, the getValue() and Field part, that is, the parameter part, have become Object objects~~

So how do you instantiate the generic type when it is specifically called? ? ? Please look at the following: ~
Insert picture description here
Here is a generic instantiation, which is the part of the main method, here we can see:

After #8 and #9 are commented, when calling the static method, Double.valueOf() and Integer.valueOf() are performed on the Object object! ~ So the instantiation is completed and the generic method is called.

Example 2: Generic with restrictions

import java.security.Security;
import java.util.Comparator;
import java.util.Scanner;

/**
 * 泛型类型的限定
 * @author jiangzl
 */
public class MyGeneric3 {
    
    

    public static <T extends Comparable> T getMax(T... e){
    
    
        if(null == e || e.length <= 0){
    
    
            return null;
        }
        T ret = e[0];
        for(T var : e){
    
    
            if(ret.compareTo(var) < 0){
    
    
                ret = var;
            }
        }
        return ret;
    }

    public static void main(String[] args) {
    
    
        int n = 5;
        Student[] s = new Student[n];
        Scanner sc = new Scanner(System.in);
        for(int i = 0;i < n;++i){
    
    
            String name = sc.next();
            int score = sc.nextInt();
            s[i] = new Student(name, score);
        }
        sc.close();
        System.out.println(getMax(s));

        String Str[] = {
    
    "b", "a", "c", "d", "e"};
        System.out.println(getMax(Str));
    }
}

class Student implements Comparable<Student> {
    
    
    private String name;
    private int score;

    public Student(String name, int score){
    
    
        this.name = name;
        this.score = score;
    }

    @Override
    public String toString(){
    
    
        return new String("name = " + name + ", score = " + score);
    }

    @Override
    public int compareTo(Student s) {
    
    
        if(score != s.score){
    
    
            return score - s.score;
        }
        else{
    
    
            return s.name.compareTo(name);
        }
    }
}

Here we require that the class that can instantiate generic methods must be a class that implements the Comparable interface! ! !

Let's take a look at the result of the disassembly: (Too much content, only excerpts)

Insert picture description here
Here we find that after the generic method ( public static <T extends Comparable> T getMax(T... e)) is disassembled, the parameter becomes the Comparable type. Here we have the following summary:

First: If there is a class in the class and interface of extends, the class must be written in front, and the type of this class is the type after disassembly!
Second: If there are no classes for extends, but interfaces, it doesn't matter. The first (closest to extends) class will be used as the generic type!

Five, covariance and contravariance

5.1. What is covariance and contravariance, and what is the relationship?

If we say that A <= B means that A is a subclass of B, let f(x) denote that a certain class x is encapsulated into a certain data structure. Such as arrays, collections, etc. Then after encapsulation, if f(A) and f(B) have an inheritance relationship, it is said to have a relationship~

The relationship is as follows:

the first:

If A <= B and f(A) <= f(B), it is called covariance, which means that A is a subtype of B, and after encapsulation, A is still a subtype of B. There are typically arrays. For example, A is a subtype of B, then array A[] is still a subtype of B[]~

second:

If A <= B and f(A) >= f(B), it is called contravariance, which means that A is a subtype of B, and after encapsulation, B is a subtype of A. Typically, it is limited by wildcards, and limited by the lower bound~

third:

If A <= B, f(A) <> f(B) is said to be unchanged, which means that A is a subclass of B. After encapsulation, A and B have no inheritance relationship. There are typically generics, unrestricted generics, and the collections after generics, such as List< A> and List< B >, have nothing to do with ~

This may not be clear, use a picture to show it:
Insert picture description here

5.2 Covariance of function return values ​​and the principle of Richter substitution

According to the Richter substitution principle, the actual parameters passed into the function should be able to be received by the formal parameters. So type (actual parameter) <= type (formal parameter), which means that the actual parameter is the type or subtype of the formal parameter itself. The set return value should be able to be received by the return value of the call, that is, type (call) >= type (return).

Look at an example:



/**
 * @author jiangzl
 */
public class MyGeneric7 {
    
    
    public static void main(String[] args) {
    
    
        ClassFather classFather = new ClassSon();
        System.out.println(classFather.method(3.14));
    }
}

class ClassFather{
    
    
    public Number method(Number value){
    
    
    	System.out.println(value.getClass());
        System.out.println("father");
        return value;
    }
}

class ClassSon extends ClassFather{
    
    
    @Override
    public Double method(Number value){
    
    
        System.out.println("son");
        return Double.valueOf(value.toString());
    }
}

Starting from JDK1.5, when subclasses are allowed to rewrite parent methods, the covariant (subclass of the return value type of the overridden parent method) is used as the return value. But let’s take a look at the output of this code:

Insert picture description here

came to a conclusion:

After JDK1.5, the subclass overrides the superclass method to allow covariance, that is, the return value is allowed to be a more specific type than the superclass method! !

5.3, array covariance

First look at the sample code:

Module 1: Class design
class FirstClass{
    
    
    public FirstClass(){
    
    
        System.out.println("FirstClass is constructed!!");
    }

    public void sayHello(){
    
    
        System.out.println("Helo World!");
    }
}

class SecondClass extends FirstClass{
    
    
    public SecondClass(){
    
    
        System.out.println("SecondClass is constructed!!");
    }
}

class LastClass extends SecondClass{
    
    
    public LastClass(){
    
    
        System.out.println("LastClass is constructed!!");
    }
}
Module 2: Covariance of arrays
		FirstClass[] firstClasses = new SecondClass[2];
        firstClasses[0] = new SecondClass();
        try {
    
    
            firstClasses[1] = new FirstClass();
            firstClasses[1].sayHello();
        }
        catch (Exception e){
    
    
            System.out.println("Runtime error!");
        }

        System.out.println();
        SecondClass[] secondClasses = new SecondClass[2];
        firstClasses = secondClasses;
        firstClasses[0] = new LastClass();
        try {
    
    
            firstClasses[1] = new FirstClass();
            firstClasses[1].sayHello();
        }
        catch (Exception e){
    
    
            System.out.println("Runtime error!");
        }

Here I only intercepted the code about the covariant part of the array, which is in the main method. The result of the execution is:
Insert picture description here
from here we can see that the array is indeed covariant, but it also raises a new problem:

Why does the subclass instantiate the object of the parent class until the parent class constructor is executed?

The compilation will pass here, and the exception will not be thrown until the parent class constructor ends! ! ! Why is the JVM so hindsight? Anyone who knows, please leave a message or send me a private message, thank you! ~

5.4, ​​generic covariance

Take a look at the example:

import java.util.ArrayList;

/**
 * @author jiangzl
 */
public class MyGeneric6 {
    
    
    public static void main(String[] args) {
    
    
        int nA = 3, nB = 4, nC = 5;
        ArrayList<A> asA = new ArrayList<>();
        for(int i = 0;i < nA;++i){
    
    
            asA.add(new A());
        }
        ArrayList<B> asB = new ArrayList<>();
        for(int i = 0;i < nB;++i){
    
    
            asB.add(new B());
        }
        ArrayList<C> asC = new ArrayList<>();
        for(int i = 0;i < nC;++i){
    
    
            asC.add(new C());
        }

        sayName(asA, asB, asC);
    }

    public static void sayName(ArrayList<? extends A>... arrayList){
    
    
        int len = arrayList.length;
        for(int i = 0;i < len;++i){
    
    
            int size = arrayList[i].size();
            for(int j = 0;j < size;++j){
    
    
                arrayList[i].get(j).sayName();
            }
        }
    }
}

class A{
    
    
    public void sayName(){
    
    
        System.out.println("my name is A");
    }
}

class B extends A{
    
    
    @Override
    public void sayName(){
    
    
        System.out.println("my name is B");
    }
}

class C extends A{
    
    
    @Override
    public void sayName(){
    
    
        System.out.println("my name is C");
    }
}

Look at the output:

Insert picture description here

5. Write at the back:

Generic covariance and contravariance, in fact, have already been mentioned in the previous section, the things to note are:

Covariance, that is, the upper bound of? Extends Class defines generics, which can only get, not set!

Contravariance, that is? The lower bound of super Class limits generics, so you can only set but not get!

If you don’t understand the basics of generics, you can refer to my last article:

Summary of JavaSE Advanced Programming-Generic Foundation 1

In addition, this article raises two doubts. Due to my limited ability, if there is a miswriting or improper understanding, I would like to ask everyone to point out. Thanks for the criticism! There are also two doubts, if there are any explanations, please feel free to enlighten me! Thanks again!

Guess you like

Origin blog.csdn.net/qq_44274276/article/details/108098107