Java learning notes 18 -- generics of collection classes

Keywords: Java set

Java programming foundation penultimate article, thank you for not giving up yourself. This column is temporarily over, and the next step is to fix the algorithm.
Zhang Huansheng, editor in chief of the reference book "Fundamentals of Java programming". The content of this book is more suitable for beginners without any foundation. There are exercises after learning a chapter, which is more suitable for beginners.
Self discipline, enthusiasm, diligence and start with a small goal that is impossible to achieve.

This article has been included in [column]


🌳 Introduction to Java series 🌳

Previous [chapter]:


🎉🎉 Java learning notes 17 - detailed summary and comparison of collection classes
✌ Java essential foundation -- 30000 word summary input and output stream
😀 Java essential foundation -- 150000 word long summary of basic knowledge points of exception handling
👋 Essential foundation of Java 14 - induction of detailed knowledge points of internal classes
🤞 Essential fundamentals of Java 13 - induction of detailed knowledge points of interface
✨ The twelfth essential foundation of Java -- abstract classes
✌ The eleventh essential foundation of Java -- polymorphic explanation and polymorphic principles
🙌 Java essential foundation ten -- detailed explanation of class inheritance
😊 Java essential foundation 9 - wrapper class
😎 Eight essential fundamentals of Java -- Math class, Random class, date and time class
👌 The seventh essential foundation of Java -- the basic and common methods of string
😜 Java essential foundation 6 - one dimensional array
👏 Java essential foundation -- encapsulation of five classes
🤳 The fourth essential foundation of Java -- member variables and local variables
👍 Java essential foundation 3 - method declaration and call, recursion
😘 Java essential foundation II -- classes and objects
👍 A necessary foundation for Java -- Fundamentals of Java language

1, What are generics

1.1 origin of generics

Before generics, when a specific Object is added to the collection, it will be regarded as an Object type. When an Object is fetched from a collection, it needs to be cast to print the corresponding Object. The disadvantages of this operation are easy to cause exceptions and bloated code.

Take a simple example that has been said countless times:

public class Example1_1 {
    public static void main(String[] args) {
        ArrayList arrayList = new ArrayList();
        arrayList.add("a");
        arrayList.add(1);

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

The output result is: ClassCastException exception

a
Exception in thread "main" java.lang.ClassCastException: java.lang.Integer cannot be cast to java.lang.String
	at com.chapter8.Example1_1.main(Example1_1.java:17)

The collection class ArrayList added String type and Integer type, but the two types were forcibly converted during output, so the program crashed. In order to solve this problem, generics are introduced.

The collection that introduces generics can remember the element type and check the element type at compile time. If an object that does not meet the type is added to the collection, the compiler will prompt an error. You can see that the generic element is defined as String type in the following figure. When adding an object of type int, the compiler prompts that String type objects are required.


It should be noted that the collection of generic types can remember the element type and check the element type during compilation. For example, understand this sentence:

public class Example1_1 {
    public static void main(String[] args) {
        ArrayList<String> arrayList = new ArrayList<>();
        ArrayList<Integer> arrayList1 = new ArrayList<>();
        arrayList1.add(1);
        arrayList.add("2");

        Class classStringArrayList = arrayList.getClass();
        Class classStringArrayList1 = arrayList1.getClass();

        if(classStringArrayList.equals(classStringArrayList1)){
            System.out.println("Same type");
        }
    }
}

You can guess the output of the program. The program will print the same type. Although the two classes add different types of generics, the two classes are still the same category. So we can make a bold guess: generics are only valid in the compilation stage. After checking the generics results, the program will erase the relevant information of generics, and the generics information will not enter the runtime stage.

1.2 advantages of generics

The origin has simply mentioned the disadvantages when there is no generics. When there is generics, the disadvantages are the advantages:

  • Reduce code bloat and make code concise
  • Eliminate many forced type conversions in source code and improve type safety in Java.

2, Simple use of generics

2.1 generic diamond syntax

List<String> list = new LinkedList<>();
Set<String> set = new HashSet<>();
Map<String,String> map = new HashMap<>();

2.2 simple examples of generics

Generics are very important for collection classes. Introducing generics into a collection can provide type safety at compile time, and there is no need to force conversion after taking elements from the collection, which simplifies the program code.

public class Demo1 {
    public static void main(String[] args) {
        List<String> ar=new ArrayList<>();
        Map<Integer,String> map = new HashMap<>();
        ar.add("a");
        ar.add("b");
        map.put(1,"c");
        map.put(2,"d");
        System.out.println("ar The collection elements of are:"+ar);
        //A method of traversing elements in a Map set
        Iterator<Map.Entry<Integer,String>> map1=map.entrySet().iterator();

        while (map1.hasNext()){
            Map.Entry<Integer,String> next = map1.next();
            System.out.println(next.getKey()+""+next.getValue());
        }
    }
}

The output results are:

ar The collection elements of are:[a, b]
1c
2d

2.3 define a generic class

2.3.1 basic writing method

class Class name<Generic identity>{
  private Generic ID
}

2.3.2 for example

/**
 * @param <T> T Represents a generic identifier, which can also be written as E and K, which can be defined by the programmer himself
 *           When instantiating a generic type, you must specify the specific type of T
 */
public class Example1_1<T>{

    /**
     * key The type of this member variable is t, and the type of T is determined externally
     */
    private T key;

    /**
     * @param key The type of generic constructor parameter key is T, which is specified externally
     */
    public Example1_1(T key){//The type of generic constructor parameter key is also t, and the type of T is also determined by the outside
        this.key = key;
    }

    /**
     * @return The return type of the generic common method getKey is t, and the type of T is specified externally
     */
    public T getKey() {
        return key;
    }

    public static void main(String[] args) {
        //The type parameter of a generic can only be a reference type, and the passed in argument type must be the same as the type of the defined generic object
        Example1_1<Integer> exampleInt= new Example1_1<>(0);
        //Error, defining generic object type must be the same as the passed in argument type
        Example1_1<Integer> exampleInt1= new Example1_1<String>("1"); 
        
        Example1_1<String> exampleString = new Example1_1<>("2");
        System.out.println("exampleInt of key Value:"+exampleInt.getKey());
        System.out.println("exampleString of key Value:"+exampleString.getKey());


 //The following descriptions are correct. The defined generic classes do not need to pass in generic arguments. In fact, generic classes can be defined into any type. If defined, restrictions will be made like the above
        Example1_1 a = new Example1_1(2);
        Example1_1 b = new Example1_1("hello");
        Example1_1 c = new Example1_1(false);

    }
}

The output result is:

exampleInt of key Value: 0
exampleString of key Value: 2

2.4 define a generic interface

2.4.1 basic writing method

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

2.4.2 for example

//Define a generic interface example1_ two
public interface Example1_2<T> {
    T fly();
}

//Next, implement this interface

/**
 * Error writing method: if the generic compiler is not declared, an error will be reported and T cannot be found
 */
class Bird implements Example1_2<T>{
    @Override
    public T fly() {
        return null;
    }
}

/**
 * @param <T> Correct writing 1, no generic argument passed in,
 *           It is the same as the definition of generic class. When declaring a class, you need to add the declaration of generic to the class
 */
class Bird1<T> implements Example1_2<T>{
    @Override
    public T fly() {
        return null;
    }
}


/**
 * Correctly write 2. Pass in generic arguments:
 * It can be seen that when the generic type is determined when implementing the interface, the generic type should be consistent wherever it is used
 * 
 */
class Bird2 implements Example1_2<String>{
    @Override
    public String fly() {
        return null;
    }
}

2.4 define a generic method

2.5.1 basic writing method

    /**
     * @param tClass Generic argument passed in
     * @param <T> The return value is of type T
     * @return
     * explain:
     *  
     */
public <T>T genericMethod(Class<T> tClass) throws Exception{
    T instance = tClass.newInstance();//The newInstance method threw an exception
    return instance;
}

See the above example to illustrate the generic method:

  • The < T > between public and the return value can be used as a generic method to declare this method
  • Only methods that declare < T > are generic methods
  • Indicates that the method will use generic type T. only then can generic type t be used in the method

2.5.2 for example

✨✨✨ The following example summarizes many correct generic methods and incorrect generic methods: if you type it on the computer, the static method will be roughly ok.

   public class Generic<T> {

    private T key;

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

    /**
     * @return This is not a generic method. Its return value is the method declared when declaring the generic class
     */
    public T getKey() {
        return key;
    }

    /**
     * Method has a problem because generic E is not declared in class declaration
     * Therefore, when using E as the type of formal parameter and return value, the compiler cannot recognize it
     */
    public E setKey(E key) {
        return key;
    }

    /**
     * @param generic Generic class reference
     * @param <T>     Generic class type
     * @return Return value
     * This is a generic method
     * public There must be a < T > between and the return value, and the return value t is declared
     * The number of generics can be any number
     */
    public <T> T showKeyName(Generic<T> generic) {
        return generic.key;
    }

    /**
     * @param generic It is not a generic method, it is a formal parameter, and the type is < T >
     */
    public void showKeyName1(Generic<T> generic) {
    }

    /**
     * @param generic Not a generic method? It is an argument, which is given when assigning a value
     */
    public void showKeyName2(Generic<?> generic) {
    }

    /**
     * There is a problem with this method because the generic type E is not declared
     *
     * @param generic Generic Class, whose type is E
     * @param <T>     Declaring generic methods
     * @return The return value type is not T
     */
    public <T> T showKeyName3(Generic<E> generic) {
    }

    /**
     * Is the correct generic method
     * Declared a generic method with no return value. Using generic E, E can be of any type
     * Since generic methods declare generics < E > when declared, the compiler can correctly identify generics < / > in generic methods even if generics are not declared in generic classes
     */
    public <E> void showKeyName3(E t) {
    }

    /**
     * Declared a generic method with no return value. Using generics, T can be of any type
     * Note that the t here is not the same type as the T defined in the generic class
     */
    public <T> void showKeyName4(T t) {
    }
    
  /**
     * Declared a generic method with no return value. Using generics, T can be multiple arbitrary types
     * showKeyName4 The input parameters can be any number or any kind
     */
    public static  <T> void showKeyName4(T... args) {
        for (T t :args){
            System.out.println(t);
        }
    }

   /**
     * For static methods that need to use generics, additional generic declarations need to be added
     * Declarations in generic classes are not allowed even if they have been used in static methods.
     * T t Be consistent with < T >
     */
    public static<T> void showKeyName5(T t) {
    }
}

2.5.3 summary

  • Generic methods enable methods to change independently of classes
  • Generic < T > must be declared in front of generic methods, except for non static void modified non return value methods
  • For static generic methods with no return value, the generic type must be declared, and the type of formal parameters of the method must be consistent with the declared generic type

2.5 generic wildcards

Let's take an example:

class Bird2<T> {

    public static void showValue(Bird2<Integer> bird2) {
        System.out.println("This is Integer type");

    }

    public static void main(String[] args) {

        Bird2<Number> bird2 = new Bird2();
        Bird2<Integer> bird1 = new Bird2();

        showValue(bird2);
        showValue(bird1);//An error will be reported here

    }
}
    

The above showValue(bird1) will report an error: the solution is to change the original Integer type to?, The problem can be solved.

  public static void showValue(Bird2<?> bird2) {
        System.out.println(bird2.fly());

Type wildcards are generally used? Instead of a specific type argument, here's? Is a type argument, not a formal parameter. It can solve problems such as the above. When the specific type is uncertain, use? Instead, assign a real type to this type argument when used.

Posted by slickboyj on Sun, 24 Oct 2021 12:15:30 -0700