java generic [finishing version]

Posted by starram on Wed, 29 Dec 2021 23:22:15 +0100

The understanding of java's generic features is still very shallow, and will be continuously improved and supplemented in the future
Reference: java generic explanation - it is definitely the most detailed explanation of generic methods, without one,Detailed explanation of Java generics: < T > and class < T >. Generic class, detailed use instance of generic method

1. General

Generics play an important role in java and are widely used in object-oriented programming and various design patterns.

What is generics? Why use generics?

Generic, or "parameterized type".


For example, getParse(Integer a), the method receives an Integer type input parameter, which can be 3, 4, or 5
However, it cannot be "abc", 3.5, etc. Therefore, generics allow the input parameters to be Integer, Double, String, etc., which is more flexible

2. A chestnut

An example that has been cited countless times:

List arrayList = new ArrayList();
arrayList.add("aaaa");
arrayList.add(100);

for(int i = 0; i< arrayList.size();i++){
    String item = (String)arrayList.get(i);
    System.out.println("Generic testing item = " + item);
}

There is no doubt that the running result of the program will end in a crash:

java.lang.ClassCastException: java.lang.Integer cannot be cast to java.lang.String

ArrayList can store any type. In the example, a String type and an Integer type are added. When they are used again, they are used in the form of String, so the program crashes. In order to solve such problems (which can be solved at the compilation stage), generics came into being.

We will change the first line of code to initialize the list, and the compiler will help us find such problems at the compilation stage.
 

List<String> arrayList = new ArrayList<String>();
...
//arrayList.add(100);  During the compilation phase, the compiler will report an error

3. Characteristics

Generics are only valid at compile time. Look at the following code:

List<String> stringArrayList = new ArrayList<String>();
List<Integer> integerArrayList = new ArrayList<Integer>();

Class classStringArrayList = stringArrayList.getClass();
Class classIntegerArrayList = integerArrayList.getClass();

if(classStringArrayList.equals(classIntegerArrayList)){
        System.out.println("Generic test types are the same");
}

Output result: the generic test type is the same

The above example proves that the program will take de genericization measures after compilation. In other words, generics in Java are only valid at the compilation stage. In the compilation process, after the generic results are correctly verified, the relevant information of the generic will be erased, and the methods of type checking and type conversion will be added at the boundary where the object enters and leaves the method. That is, generic information does not enter the runtime phase.

This can be summed up in one sentence: generic types are logically regarded as multiple different types, which are actually the same basic types.

4. Generic, must < > be used?

When using ArrayList, if the generic type is not defined, the generic type is actually Object

At this point, < T > can only be used as an Object without giving play to the advantages of generics

However: when we define the generic type < string >, the compiler will perform strong verification

5. Use of generics

There are three ways to use generics: generic class, generic interface and generic method

5.1} generic classes

Generic types are used in the definition of classes and are called generic classes.

One of the most common generic classes:

//Here, T can be written as any identifier. Common parameters such as T, E, K and V are often used to represent generics
//When instantiating a generic class, you must specify the concrete type of T
public class Generic<T>{ 
    //key the type of this member variable is t, and the type of T is specified externally  
    private T key;

    public Generic(T key) { //The type of generic constructor parameter key is also t, and the type of T is specified externally
        this.key = key;
    }

    public T getKey(){ //The return value type of the generic method getKey is t, and the type of T is specified externally
        return key;
    }
}
//Type parameters of generic types can only be class types (including custom classes), not simple types
//The type of the passed in argument must be the same as that of the generic type parameter, that is, Integer
Generic<Integer> genericInteger = new Generic<Integer>(123456);

//The input argument type must be the same as the type parameter type of the generic type, that is, String
Generic<String> genericString = new Generic<String>("key_vlaue");
System.out.println("Generic testing key is " + genericInteger.getKey());
System.out.println("Generic testing key is " + genericString.getKey());
Generic testing key is 123456
 Generic testing key is key_vlaue

5.2} generic interfaces

The definition and use of generic interfaces and generic classes are basically the same. Generic interfaces are often used in various producers. See an example:

//Define a generic interface
public interface Generator<T> {
    public T next();
}

When a class implementing a generic interface does not pass in a generic argument:

/**
 * When a generic argument is not passed in, it is the same as the definition of a generic class. When declaring a class, you need to add the declaration of the generic to the class
 * Namely: class fruitgenerator < T > implements generator < T >{
 * If you do not declare a generic type, such as: class fruitgenerator implements generator < T >, the compiler will report an error: "Unknown class"
 */
class FruitGenerator<T> implements Generator<T>{
    @Override
    public T next() {
        return null;
    }
}

When a class implementing a generic interface passes in a generic argument:

/**
 * When passing in generic arguments:
 * Define a producer to implement this interface, although we have only created a generic interface generator < T >
 * However, we can pass in countless arguments for T to form countless types of Generator interfaces.
 * When implementing a class to implement a generic interface, if a generic type has been passed into an argument type, all places where generics are used must be replaced with the passed in argument type
 * Namely: generator < T >, public T next(); The T in should be replaced with the incoming String type.
 */
public class FruitGenerator implements Generator<String> {

    private String[] fruits = new String[]{"Apple", "Banana", "Pear"};

    @Override
    public String next() {
        Random rand = new Random();
        return fruits[rand.nextInt(3)];
    }
}

5.3 generic methods

public class GenericTest {
   //This class is a generic class, which has been introduced above
   public class Generic<T>{     
        private T key;
 
        public Generic(T key) {
            this.key = key;
        }
 
        //What I want to say is this. Although generics are used in the method, it is not a generic method.
        //This is just an ordinary member method in the class, but its return value is the declared generic of the declared generic class.
        //Therefore, we can continue to use the generic type T in this method.
        public T getKey(){
            return key;
        }
    }
 
    /** 
     * This is a real generic method.
     * First, the < T > between public and the return value is essential, which indicates that this is a generic method and declares a generic t
     * This T can appear anywhere in the generic method
     */
    public <T> T showKeyName(Generic<T> container){
        System.out.println("container key :" + container.getKey());
        //Of course, this example is not appropriate, just to illustrate the characteristics of generic methods.
        T test = container.getKey();
        return test;
    }

    */
 
    public static void main(String[] args) {
 
 
    }
}

5.4 wildcards

Let's be clear: wildcards are used for methods

 

Type wildcards are generally used? Instead of specific type parameters. For example, list <? > Logically, it is the parent class of all list < concrete type arguments > such as list < string >, list < integer >.

package com.luoyu.aiyu.luo1;

import java.util.ArrayList;
import java.util.List;

public class TestMain1
{
    public static void main(String[] args) {
        List<String> name = new ArrayList<String>();
        List<Integer> age = new ArrayList<Integer>();
        List<Number> number = new ArrayList<Number>();

        name.add("icon");
        age.add(18);
        number.add(314);

        getData(name);
        getData(age);
        getData(number);

    }

    public static void getData(List<?> data) {
        System.out.println("data :" + data.get(0));
    }
}

Operation results:

data :icon
data :18
data :314   

5.5 wildcard upper limit

The parameter in getUperNumber() method has limited the upper limit of parameter genericity to Number. Therefore, if String is not in this range, an error will be reported

That is, the support parameter supports Number and its subclasses

5.6 wildcard lower limit

The lower limit of the type wildcard is defined by the form "list <? Super Number >, which means that the type can only accept instances of "Number" and its upper parent type, such as "Object".

5.7 static method generics

For the following static methods: testmain1 < T > and static methods < T > It doesn't matter anymore. It is highly recommended to change it to other marks

public class TestMain1<T>
{
    public T key;

    public static <T> void staticTest(T a){
        System.out.println(a.toString());
    }

    public TestMain1(T key){
        this.key = key;
    }

    public T getKey(){
        return key;
    }

    public static void main(String[] args) {
        //Validate static methods
        TestMain1.<String>staticTest("123");

        TestMain1<Integer> integerTestMain1 = new TestMain1<>(456);
        System.out.println(integerTestMain1.getKey());
    }
}

Operation results:

123
456 

After modification: the result runs normally

package com.luoyu.aiyu.luo1;

import java.util.Arrays;

public class TestMain1<T>
{
    public T key;

    public static <K> void staticTest(K a){
        System.out.println(a.toString());
    }

    public TestMain1(T key){
        this.key = key;
    }

    public T getKey(){
        return key;
    }

    public static void main(String[] args) {
        //Validate static methods
        TestMain1.<String>staticTest("123");

        TestMain1<Integer> integerTestMain1 = new TestMain1<>(456);
        System.out.println(integerTestMain1.getKey());
    }
}

 

Topics: Java Back-end