This article will give you an in-depth understanding of [Java Basics] Generics

written in front


        Hello everyone, I am [ Lin-Xiaobai ], a student majoring in software engineering , who likes computer knowledge . I hope everyone can learn and progress together ! I am a college student , and my professional level is limited. If you find any mistakes or deficiencies , please correct me! thank you all! ! !

        If brothers and sisters are interested in my articles, please don't be stingy with your little hands, give more likes and follow ! ❤❤❤ Love you guys! ! !


Table of contents

written in front

1. Why should there be generics?

1.1 Why should there be generics

1.2 The concept of generics

1.3 Using generics in collections

1.4 Generic Code Demonstration

 2. Custom generic structure

2.1 Generic structure

2.2 Generic class, generic interface

2.3 Generic methods

2.4 Custom generic type code demonstration

2.5 The embodiment of generics in inheritance

3. Use of wildcards

3.1 Using wildcards

3.2 Points to note

3.3 Restricted wildcards

3.4 Wildcard Code Demonstration

4. Examples of generic applications

4.1 Generic nesting

4.2 Actual case

epilogue


【Past review】

This article will give you an in-depth understanding of [Java Basics] Java Collections (Part 1)

This article will give you a deep understanding of [Java Basics] Notes

This article will give you a deep understanding of [Java Basics] Enumeration Classes

This article will give you a deep understanding of [Java Basics] Commonly used classes (Part 1)

This article will give you an in-depth understanding of [Java Basics] Multithreading (Part 1)

This article will give you a deep understanding of [Java Basics] Exception Handling


1. Why should there be generics?


1.1  Why should there be generics

  • Generics: Tags
  • Example:
    • Chinese medicine store, with labels on the outside of each drawer
    • A lot of bottles on a shopping shelf in a supermarket, what does each bottle contain, with a label
  • Generic Design Background
  • The collection container class cannot determine what type of object the container actually stores in the design phase / declaration phase, so before JDK1.5 , the element type can only be designed as Object , and after JDK1.5 , use generics to solve it. Because at this time, except for the type of the element, other parts are determined, such as how to save and manage the element, etc., so the type of the element is designed as a parameter at this time, and this type parameter is called generic . Collection<E> , List<E> , ArrayList<E> This <E> is the type parameter, that is, generic.

1.2  The concept of generics

  • The so-called generic type is to allow the type of a certain attribute in a class or the return value and parameter type of a certain method to be represented by an identifier when defining a class or an interface. This type parameter will be determined at the time of use (for example, when inheriting or implementing this interface, declaring variables with this type, creating objects) (that is, passing in actual type parameters, also known as type arguments).
  • Since JDK1.5 , Java has introduced the concept of "parameterized type ( Parameterized type )", which allows us to specify the type of collection elements when creating a collection, just like: List<String> , which indicates that the List can only hold strings type of object.
  • JDK1.5 rewrites all interfaces and classes in the collection framework, and adds generic support to these interfaces and classes, so that type arguments can be passed in when declaring collection variables and creating collection objects.

So why should there be generics? Can't direct Objects also store data?

1. Solve the security problem of element storage, such as commodity and drug labels, which cannot be mistaken.
2. Solve the problem of mandatory type conversion when obtaining data elements, such as not having to identify commodities and medicines every time.

Java generics can guarantee that if the program does not issue warnings at compile time, no ClassCastException will be generated at runtime. At the same time, the code is more concise and robust.

1.3 Using generics in collections

ArrayList<Integer> list = new ArrayList<>();//类型推断
list.add(78);
list.add(88);
list.add(77);
list.add(66);
//遍历方式一:
//for(Integer i : list) {
    //不需要强转
    //System.out.println(i);
//}
//遍历方式二:
Iterator<Integer> iterator = list.iterator();
while(iterator.hasNext()) {
    System.out.println(iterator.next());
}
Map<String,Integer> map = new HashMap<String,Integer>();
map.put("Tom1",34);
map.put("Tom2",44);
map.put("Tom3",33);
map.put("Tom4",32);
//添加失败
//map.put(33, "Tom");
Set<Entry<String,Integer>> entrySet = map.entrySet();
Iterator<Entry<String,Integer>> iterator = entrySet.iterator();
while(iterator.hasNext()) {
    Entry<String,Integer> entry = iterator.next();
    System.out.println(entry.getKey() + "--->" + entry.getValue());
}

1.4 Generic Code Demonstration

import org.junit.Test;

import java.util.*;

/**
 * 泛型的使用
 * 1.jdk 5.0新增的特性
 *
 * 2.在集合中使用泛型:
 *  总结:
 *  ① 集合接口或集合类在jdk5.0时都修改为带泛型的结构。
 *  ② 在实例化集合类时,可以指明具体的泛型类型
 *  ③ 指明完以后,在集合类或接口中凡是定义类或接口时,内部结构(比如:方法、构造器、属性等)使用到类的泛型的位置,都指定为实例化的泛型类型。
 *    比如:add(E e)  --->实例化以后:add(Integer e)
 *  ④ 注意点:泛型的类型必须是类,不能是基本数据类型。需要用到基本数据类型的位置,拿包装类替换
 *  ⑤ 如果实例化时,没有指明泛型的类型。默认类型为java.lang.Object类型。
 *
 * 3.如何自定义泛型结构:泛型类、泛型接口;泛型方法。见 GenericTest1.java
 */
public class GenericTest {

    //在集合中使用泛型之前的情况:
    @Test
    public void test1() {
        ArrayList list = new ArrayList();
        //需求:存放学生的成绩
        list.add(78);
        list.add(76);
        list.add(89);
        list.add(88);
        //问题一:类型不安全
        //list.add("Tom");

        for (Object score : list) {
            //问题二:强转时,可能出现ClassCastException
            int stuScore = (Integer) score;
            System.out.println(stuScore);
        }
    }

    //在集合中使用泛型的情况:以ArrayList为例
    @Test
    public void test2() {
        ArrayList<Integer> list = new ArrayList<Integer>();

        list.add(78);
        list.add(87);
        list.add(99);
        list.add(65);
        //编译时,就会进行类型检查,保证数据的安全
//        list.add("Tom");

        //方式一:
//        for(Integer score : list) {
//            //避免了强转操作
//            int stuScore = score;
//            System.out.println(stuScore);
//        }
        //方式二:
        Iterator<Integer> iterator = list.iterator();
        while (iterator.hasNext()) {
            int stuScore = iterator.next();
            System.out.println(stuScore);
        }
    }

    //在集合中使用泛型的情况:以HashMap为例
    @Test
    public void test3() {
        //Map<String,Integer> map = new HashMap<String,Integer>();
        //jdk7新特性:类型推断
        Map<String, Integer> map = new HashMap<>();

        map.put("Tom", 87);
        map.put("Jerry", 87);
        map.put("Jack", 67);

//      map.put(123,"ABC");
        //泛型的嵌套
        Set<Map.Entry<String, Integer>> entry = map.entrySet();
        Iterator<Map.Entry<String, Integer>> iterator = entry.iterator();

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

 2. Custom generic structure


2.1  Generic structure

1. Generic declaration
  • interface List<T> class GenTest<K,V>
  • Among them, T, K, and V do not represent values, but represent types. Any letter can be used here.
  • Commonly used T said, is the abbreviation of Type .
2. Generic instantiation:
  • Be sure to specify the value (type) of the type parameter after the class name. like:
    • List<String> strList = new ArrayList<String>();
    • Iterator<Customer> iterator = customers.iterator();
  • T can only be a class and cannot be filled with primitive data types. but can be filled using the wrapper class
  • Restricting the contents of a collection to a specific data type is the core idea behind generics
  •  Experience: The main advantage of using generics is the ability to detect errors at compile time rather than at runtime.

2.2  Generic class, generic interface

1. A generic class may have multiple parameters. In this case, multiple parameters should be placed in angle brackets together. For example: <E1,E2,E3>
2. The constructor of the generic class is as follows: public GenericClass(){} .
    And the following is wrong: public GenericClass<E>(){}
3. After instantiation, the structure operating the original generic location must be consistent with the specified generic type.
4. References with different generic types cannot assign values ​​to each other. Although ArrayList<String> and ArrayList<Integer> are two types at compile time , only one ArrayList is loaded into the JVM at runtime .
5. If the generic type is not specified, it will be erased. The type corresponding to the generic type is treated as Object , but it is not equivalent to Object . Experience: Generics should be used all the way. If not, don't use it all the way.
6. If the generic structure is an interface or an abstract class, objects of the generic class cannot be created.
7. jdk1.7 , simplified operation of generics: ArrayList<Fruit> flist = new ArrayList<>();
8. Basic data types cannot be used in the specification of generics, and wrapper classes can be used instead.
9. The generic type declared on the class / interface represents a certain type in this class or interface, and can be used as the type of non-static properties, the parameter type of non-static methods, and the return value type of non-static methods. But you cannot use class generics in static methods.
10. Exception classes cannot be generic
11. New E[] cannot be used . But it works: E[] elements = (E[])new Object[capacity];
Reference: The statement in the ArrayList source code: Object[] elementData , not an array of generic parameter types.
12. The parent class has a generic type, and the subclass can choose to retain the generic type or specify the generic type:
  • The subclass does not retain the generic type of the parent class: implement on demand
    • no type erasure
    • specific type
  • The subclass retains the generic type of the parent class: generic subclass
    • keep all
    • partially reserved
Conclusion: The subclass must be a "rich second generation". In addition to specifying or retaining the generic type of the parent class, the subclass can also add its own generic type
class Father<T1, T2> {
}
// 子类不保留父类的泛型
// 1)没有类型 擦除
class Son<A, B> extends Father {//等价于class Son extends Father<Object,Object> {
}
// 2)具体类型
class Son2<A, B> extends Father<Integer, String> {
}
// 子类保留父类的泛型
// 1)全部保留
class Son3<T1, T2, A, B> extends Father<T1, T2> {
}
// 2)部分保留
class Son4<T2, A, B> extends Father<Integer, T2> {
}    
class Person<T> {
    // 使用T类型定义变量
    private T info;
    // 使用T类型定义一般方法
    public T getInfo() {
    return info;
}
public void setInfo(T info) {
    this.info = info;
}
// 使用T类型定义构造器
public Person() {
}
public Person(T info) {
    this.info = info;
}
// static的方法中不能声明泛型
//public static void show(T t) {
//
//}
// 不能在try-catch中使用泛型定义
//public void test() {
//try {
//
//} catch (MyException<T> ex) {
//
//}
//}
//}


2.3  Generic methods

  • Methods can also be genericized, regardless of whether the class defined in them is a generic class or not. Generic parameters can be defined in a generic method. At this time, the type of the parameter is the type of the incoming data.
The format of a generic method:
  • [ Access ] <generic> return type method name ([ generic identification parameter name ] ) thrown exception
  • Generic methods can also specify an upper limit when declaring generics ( in 12.5 )
public class DAO {
    public <E> E get(int id, E e) {
        E result = null;
        return result;
    }
}
public static <T> void fromArrayToCollection(T[] a, Collection<T> c) {
    for (T o : a) {
        c.add(o);
    }
}
public static void main(String[] args) {
    Object[] ao = new Object[100];
    Collection<Object> co = new ArrayList<Object>();
    fromArrayToCollection(ao, co);
    String[] sa = new String[20];
    Collection<String> cs = new ArrayList<>();
    fromArrayToCollection(sa, cs);
    Collection<Double> cd = new ArrayList<>();
    // 下面代码中T是Double类,但sa是String类型,编译错误。
    // fromArrayToCollection(sa, cd);
    // 下面代码中T是Object类型,sa是String类型,可以赋值成功。
    fromArrayToCollection(sa, co);
}
class Creature{}
class Person extends Creature{}
class Man extends Person{}
class PersonTest {
    public static <T extends Person> void test(T t) {
        System.out.println(t);
    }
    public static void main(String[] args) {
        test(new Person());
        test(new Man());
        //The method test(T) in the type PersonTest is not 
        //applicable for the arguments (Creature)
        test(new Creature());
    }
}

2.4 Custom generic type code demonstration

import org.junit.Test;

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

/** 如何自定义泛型结构:泛型类、泛型接口;泛型方法。
 *
 * 1. 关于自定义泛型类、泛型接口:
 */
public class GenericTest1 {

    @Test
    public void test1() {
        //如果定义了泛型类,实例化没有指明类的泛型,则认为此泛型类型为Object类型
        //要求:如果大家定义了类是带泛型的,建议在实例化时要指明类的泛型。
        Order order = new Order();
        order.setOrderT(123);
        order.setOrderT("ABC");

        //建议:实例化时指明类的泛型
        Order<String> order1 = new Order<String>("orderAA", 1001, "order:AA");
        order1.setOrderT("AA:hello");
    }

    @Test
    public void test2() {
        SubOrder sub1 = new SubOrder();
        //由于子类在继承带泛型的父类时,指明了泛型类型。则实例化子类对象时,不再需要指明泛型。
        sub1.setOrderT(1122);

        SubOrder1<String> sub2 = new SubOrder1<>();
        sub2.setOrderT("order2...");
    }

    @Test
    public void test3() {
        ArrayList<String> list1 = null;
        ArrayList<Integer> list2 = new ArrayList<Integer>();
        //泛型不同的引用不能相互赋值。
        //list1 = list2;

        Person p1 = null;
        Person p2 = null;
        p1 = p2;
    }

    //测试泛型方法
    @Test
    public void test4() {
        Order<String> order = new Order<>();
        Integer[] arr = new Integer[]{1, 2, 3, 4};
        //泛型方法在调用时,指明泛型参数的类型。
        List<Integer> list = order.copyFromArrayToList(arr);

        System.out.println(list);
    }
}
import java.util.ArrayList;
import java.util.List;

/**
 * 自定义泛型类
 */
public class Order<T> {
    String orderName;
    int orderId;
    //类的内部结构就可以使用类的泛型

    T orderT;

    public Order() {
        //编译不通过
        //T[] arr = new T[10];
        //编译通过
        T[] arr = (T[]) new Object[10];
    }

    public Order(String orderName, int orderId, T orderT) {
        this.orderName = orderName;
        this.orderId = orderId;
        this.orderT = orderT;
    }

    //如下的三个方法都不是泛型方法
    public T getOrderT() {
        return orderT;
    }

    public void setOrderT(T orderT) {
        this.orderT = orderT;
    }

    @Override
    public String toString() {
        return "Order{" +
                "orderName='" + orderName + '\'' +
                ", orderId=" + orderId +
                ", orderT=" + orderT +
                '}';
    }
//    静态方法中不能使用类的泛型。
//    public static void show(T orderT) {
//        System.out.println(orderT);
//    }

    public void show() {
        //编译不通过
//        try{
//
//        } catch(T t) {
//
//        }
    }

    //泛型方法:在方法中出现了泛型的结构,泛型参数与类的泛型参数没有任何关系。
    //换句话说,泛型方法所属的类是不是泛型类都没有关系。
    //泛型方法,可以声明为静态的。原因:泛型参数是在调用方法时确定的。并非在实例化类时确定。
    public static <E> List<E> copyFromArrayToList(E[] arr) {
        ArrayList<E> list = new ArrayList<>();
        for (E e : arr) {
            list.add(e);
        }
        return list;
    }
}
import java.util.ArrayList;
import java.util.List;

public class SubOrder extends Order<Integer> {//SubOrder:不是泛型类
    public static <E> List<E> copyFromArrayToList(E[] arr) {
        ArrayList<E> list = new ArrayList<>();
        for (E e : arr) {
            list.add(e);
        }
        return list;
    }
}
public class SubOrder1<T> extends Order<T> {//SubOrder1<T>:仍然是泛型类
}

2.5 The embodiment of generics in inheritance

  • If B is a subtype (subclass or subinterface) of A , and G is a class or interface with a generic declaration, G<B> is not a subtype of G<A> !
  • For example: String is a subclass of Object , but List<String> is not a subclass of List<Object>.
public void testGenericAndSubClass() {
    Person[] persons = null;
    Man[] mans = null;
    // 而 Person[] 是 Man[] 的父类.
    persons = mans;
    Person p = mans[0];
    // 在泛型的集合上
    List<Person> personList = null;
    List<Man> manList = null;
    // personList = manList;(报错)
}

3. Use of wildcards


3.1 Using wildcards

1. Use type wildcards: ?

  • For example: List<?> Map<?,?>
  • List<?> is the parent class of various generic Lists such as List<String> and List<Object> .

2. When reading the elements in the object list of List<?> , it is always safe, because no matter what the real type of the list is, it contains Object .

3. When writing to the elements in the list , no. Since we don't know the element type of c , we cannot add objects to it.

  • The only exception is null, which is a member of all types.

Adding arbitrary elements to it is not type-safe :

  • Collection<?> c = new ArrayList<String>();
  • c.add(new Object()); // compile time error
  • Since we don't know the element type of c , we cannot add objects to it. The add method has a type parameter E as the element type of the collection. Any argument we pass to add must be a subclass of an unknown type. Since we don't know what type that is, we can't pass anything in.

The only exception is null , which is a member of all types.

On the other hand, we can call the get() method and use its return value. The return value is an unknown type, but as we know, it is always an Object .

public static void main(String[] args) {
    List<?> list = null;
    list = new ArrayList<String>();
    list = new ArrayList<Double>();
    // list.add(3);//编译不通过
    list.add(null);
    List<String> l1 = new ArrayList<String>();
    List<Integer> l2 = new ArrayList<Integer>();
    l1.add("尚硅谷");
    l2.add(15);
    read(l1);
    read(l2);
}
public static void read(List<?> list) {
    for (Object o : list) {
        System.out.println(o);
    }
}

3.2 Points to note

Note 1 : Compilation error: cannot be used in generic method declarations, <> cannot be used in front of the return value type ?
public static <?> void test(ArrayList<?> list) {
}
Note 2 : Compilation error: cannot be used in the declaration of a generic class
class GenericTypeClass<?> {
}
Note 3 : Compilation error: cannot be used to create objects, and the right side belongs to the creation of collection objects
ArrayList<?> list2 = new ArrayList<?>();

3.3  Restricted wildcards

<?>
  • Allow all generic call-by-reference
Maximum number of tokens specified
  • Upper limit extends : The type specified when using must inherit a certain class or implement a certain interface, ie <=
Wildcards specify lower bounds
  • Lower limit super : The specified type cannot be smaller than the class of the operation, ie >=
Example:
<? extends Number> ( infinitesimal , Number]
  • Only generic reference calls for Number and Number subclasses are allowed
<? super Number> [Number , infinity )
  • Only generic types are allowed to be called by reference of Number and the parent class of Number
<? extends Comparable>
  • Generics are only allowed to be called by references to implementation classes that implement the Comparable interface
public static void printCollection3(Collection<? extends Person> coll) {
    //Iterator只能用Iterator<?>或Iterator<? extends Person>.why?
    Iterator<?> iterator = coll.iterator();
    while (iterator.hasNext()) {
        System.out.println(iterator.next());
    }
}
public static void printCollection4(Collection<? super Person> coll) {
    //Iterator只能用Iterator<?>或Iterator<? super Person>.why?
    Iterator<?> iterator = coll.iterator();
    while (iterator.hasNext()) {
        System.out.println(iterator.next());
    }
}

3.4 Wildcard Code Demonstration

import org.junit.Test;

import java.util.AbstractList;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

/**
 * 1. 泛型在继承方面的体现
 * 2. 通配符的使用
 */
public class GenericTest {
    /*
    1. 泛型在继承方面的体现
       虽然类A是类B的父类,但是G<A> 和G<B>二者不具备子父类关系,二者是并列关系。
       补充:类A是类B的父类,A<G> 是 B<G> 的父类
     */
    @Test
    public void test1() {
        Object obj = null;
        String str = null;
        obj = str;

        Object[] arr1 = null;
        String[] arr2 = null;
        arr1 = arr2;
//      编译不通过
//      Date date = new Date();
//      str = date;
        List<Object> list1 = null;
        List<String> list2 = new ArrayList<String>();
        //此时的list1和list2的类型不具有子父类关系
        //编译不通过
        //list1 = list2;
        /*
        反证法:
        假设list1 = list2;
           list1.add(123);导致混入非String的数据。出错。
         */
        show(list1);
        show1(list2);
    }

    public void show1(List<String> list) {

    }

    public void show(List<Object> list) {

    }

    @Test
    public void test2() {
        AbstractList<String> list1 = null;
        List<String> list2 = null;
        ArrayList<String> list3 = null;

        list1 = list3;
        list2 = list3;

        List<String> list4 = new ArrayList<>();
    }

    /*
    2. 通配符的使用
       通配符:?
       类A是类B的父类,G<A>和G<B>是没有关系的,二者共同的父类是:G<?>
     */

    @Test
    public void test3() {
        List<Object> list1 = null;
        List<String> list2 = null;

        List<?> list = null;

        list = list1;
        list = list2;
        //编译通过
        //print(list1);
        //print(list2);

        List<String> list3 = new ArrayList<>();
        list3.add("AA");
        list3.add("BB");
        list3.add("CC");
        list = list3;
        //添加(写入):对于List<?>就不能向其内部添加数据。
        //除了添加null之外。
        //list.add("DD");
        //list.add('?');

        list.add(null);

        //获取(读取):允许读取数据,读取的数据类型为Object。
        Object o = list.get(0);
        System.out.println(o);
    }

    public void print(List<?> list) {
        Iterator<?> iterator = list.iterator();
        while (iterator.hasNext()) {
            Object obj = iterator.next();
            System.out.println(obj);
        }
    }

    /*
    3.有限制条件的通配符的使用。
        ? extends A:
                G<? extends A> 可以作为G<A>和G<B>的父类,其中B是A的子类

        ? super A:
                G<? super A> 可以作为G<A>和G<B>的父类,其中B是A的父类
     */
    @Test
    public void test4() {
        List<? extends Person> list1 = null;
        List<? super Person> list2 = null;

        List<Student> list3 = new ArrayList<Student>();
        List<Person> list4 = new ArrayList<Person>();
        List<Object> list5 = new ArrayList<Object>();

        list1 = list3;
        list1 = list4;
        //list1 = list5;

        //list2 = list3;
        list2 = list4;
        list2 = list5;

        //读取数据:
        list1 = list3;
        Person p = list1.get(0);
        //编译不通过
        //Student s = list1.get(0);

        list2 = list4;
        Object obj = list2.get(0);
        //编译不通过
        //Person obj = list2.get(0);

        //写入数据:
        //编译不通过
        //list1.add(new Student());

        //编译通过
        list2.add(new Person());
        list2.add(new Student());
    }
}

4. Examples of generic applications


4.1  Generic nesting

public static void main(String[] args) {
    HashMap<String, ArrayList<Citizen>> map = new HashMap<String, ArrayList<Citizen>>();
    ArrayList<Citizen> list = new ArrayList<Citizen>();
    list.add(new Citizen("刘恺威"));
    list.add(new Citizen("杨幂"));
    list.add(new Citizen("小糯米"));
    map.put("刘恺威", list);
    Set<Entry<String, ArrayList<Citizen>>> entrySet = map.entrySet();
    Iterator<Entry<String, ArrayList<Citizen>>> iterator = entrySet.iterator();
    while (iterator.hasNext()) {
        Entry<String, ArrayList<Citizen>> entry = iterator.next();
        String key = entry.getKey();
        ArrayList<Citizen> value = entry.getValue();
        System.out.println("户主:" + key);
        System.out.println("家庭成员:" + value);
    }
}

4.2  Actual case

Users often use class associations when designing classes. For example, an attribute of information can be defined in a person, but a person may have various information (such as contact information, basic information, etc.), so this information The type of attributes can be declared through generics, and then only the corresponding information classes need to be designed.


epilogue


I will continue to update the article! I hope everyone will click three times , your encouragement is the motivation for the author to keep updating

Guess you like

Origin blog.csdn.net/qq_34025246/article/details/128164961