Use of generics in java (with explanation and exercise)

Posted by JustinK101 on Mon, 03 Jan 2022 05:38:29 +0100

generic paradigm

Definition of generics

 the so-called genericity is to allow the type of an attribute in a class or the return value and parameter type of a method to be represented by an identifier when defining a class and interface. This type parameter will be determined when it is used (for example, inheriting or implementing this interface, declaring variables and creating objects with this type) (that is, passing in the actual type parameter, also known as the type argument).
 from jdk1 After 5, Java introduced the concept of "Parameterized type", which allows us to specify the type of collection elements when creating a collection, such as List, which indicates that the List can only store objects of string type.
 JDK1.5 rewrites all interfaces and classes in the collection framework and adds generic support for these interfaces and classes, so that type arguments can be passed in when declaring collection variables and creating collection objects.

Use generics in Collections

1. New features in JDK 5.0

2. Use generics in the collection:
Summary:
① Collection interface or collection class in jdk5 0 is modified to a structure with generics.
② When instantiating a collection class, you can specify a specific generic type
③ After specifying, when defining a class or interface in a collection class or interface, the location where the generic type of the class is used by the internal structure (such as method, constructor, attribute, etc.) is specified as the instantiated generic type.
For example: add(E e) - > after instantiation: add(Integer e)
④ Note: the type of a generic type must be a class, not a basic data type. Where the basic data type needs to be used, replace it with a wrapper class
⑤ If the generic type is not specified when instantiating. The default type is Java Lang. object type.

3. How to customize generic structure: generic class and generic interface; Generic methods. See generictest1 java

Here are some examples of using generics in collections.

public class GenericTest {


    //Before using generics in a collection:
    @Test
    public void test1(){
        ArrayList list = new ArrayList();
        //Requirements: store students' grades
        list.add(78);
        list.add(76);
        list.add(89);
        list.add(88);
        //Problem 1: unsafe type
//        list.add("Tom");

        for(Object score : list){
            //Problem 2: ClassCastException may occur during forced rotation
            int stuScore = (Integer) score;

            System.out.println(stuScore);

        }

    }

    //Using generics in Collections: take ArrayList as an example
    @Test
    public void test2(){
       ArrayList<Integer> list =  new ArrayList<Integer>();

        list.add(78);
        list.add(87);
        list.add(99);
        list.add(65);
        //When compiling, type checking will be carried out to ensure the safety of data
//        list.add("Tom");

        //Mode 1:
//        for(Integer score : list){
//            //Strong rotation operation is avoided
//            int stuScore = score;
//
//            System.out.println(stuScore);
//
//        }
        //Mode 2:
        Iterator<Integer> iterator = list.iterator();
        while(iterator.hasNext()){
            int stuScore = iterator.next();
            System.out.println(stuScore);
        }

    }

    //Using generics in Collections: take HashMap as an example
    @Test
    public void test3(){
//        Map<String,Integer> map = new HashMap<String,Integer>();
        //jdk7 new feature: type inference
        Map<String,Integer> map = new HashMap<>();

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

//        map.put(123,"ABC");
        //Nesting of generics
        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);
        }

    }


}

Custom generic structure

Custom generic structure: generic class, generic interface

  1. A generic class may have multiple parameters. In this case, multiple parameters should be placed together in angle brackets. For example: < E1, E2, E3 >
  2. The constructor of the generic class is as follows: public GenericClass() {}. The following is an error: public GenericClass() {}
  3. After instantiation, the structure of the original generic location must be consistent with the specified generic type.
  4. References of different generics cannot be assigned to each other. Although ArrayList and ArrayList are two types at compile time, only one ArrayList is loaded into the JVM at run time.
  5. If a generic type is not specified, it will be erased. The types corresponding to the generic type are treated as Object, but they are not equivalent to Object. Experience: generics should be used all the way. No, not all the way.
  6. If the generic structure is an interface or abstract class, you cannot create an object of the generic class.
  7. jdk1.7. Simplified operation of generics: ArrayList flist = new ArrayList < > ();
  8. Basic data types cannot be used in the specification of generic types, but can be replaced by wrapper classes.
  9. The generic type declared on the class / interface represents a type in this class or interface, and can be used as the type of non static attribute, parameter type of non static method and return value type of non static method. However, you cannot use generics of classes in static methods.
  10. Exception class cannot be generic
  11. Cannot use new E []. However, you can: E[] elements = (E[])new Object[capacity]; Reference: the ArrayList source code declares: Object[] elementData, not a generic parameter type array.
    12. The parent class has a generic type, and the child class can choose to keep the generic type or specify the generic type:
    >Subclasses do not retain the genericity of the parent class: on-demand implementation
     no type erasure
     specific type
    >Subclasses retain the genericity of the parent class: generic subclasses
     all reserved
     partial reservation
    Conclusion: the subclass must be a "rich second generation". In addition to specifying or retaining the generics of the parent class, the subclass can also add its own generics

Custom generic structure: generic methods
 methods can also be generalized, regardless of whether the class defined in them is a generic class or not. Generic parameters can be defined in generic methods. In this case, the type of parameter is the type of incoming data.
 format of generic method:
Exception thrown by [access permission] < generic > return type method name ([generic ID parameter name])
 the upper limit can also be specified when declaring a generic method (described in 12.5)

The following shows the invocation of some generic classes, generic interfaces and generic methods.

public class GenericTest1 {

    @Test
    public void test1(){
        //If a generic class is defined and the instantiation does not specify the generic type of the class, the generic type is considered to be Object type
        //Requirement: if you have defined that the class is generic, it is recommended to specify the generic type of the class when instantiating.
        Order order = new Order();
        order.setOrderT(123);
        order.setOrderT("ABC");

        //Suggestion: specify the generic type of the class when instantiating
        Order<String> order1 = new Order<String>("orderAA",1001,"order:AA");

        order1.setOrderT("AA:hello");

    }

    @Test
    public void test2(){
        SubOrder sub1 = new SubOrder();
        //Because the subclass indicates the generic type when inheriting the parent class with generics. When instantiating subclass objects, you no longer need to specify generics.
        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>();
        //References of different generics cannot be assigned to each other.
//        list1 = list2;

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


    }

    //Test generic methods
    @Test
    public void test4(){
        Order<String> order = new Order<>();
        Integer[] arr = new Integer[]{1,2,3,4};
        //When a generic method is called, it indicates the type of generic parameter.
        List<Integer> list = order.copyFromArrayToList(arr);

        System.out.println(list);
    }
}

Here are some custom generic structures: generic classes, generic interfaces, and generic methods.

public class Order<T> {

    String orderName;
    int orderId;

    //The internal structure of the class can use the generics of the class

    T orderT;

    public Order(){
        //Compilation failed
//        T[] arr = new T[10];
        //Compile passed
        T[] arr = (T[]) new Object[10];
    }

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

    //None of the following three methods are generic methods
    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 +
                '}';
    }
    //Generics of classes cannot be used in static methods.
//    public static void show(T orderT){
//        System.out.println(orderT);
//    }

    public void show(){
        //Compilation failed
//        try{
//
//
//        }catch(T t){
//
//        }

    }

    //Generic method: the generic structure appears in the method, and the generic parameters have nothing to do with the generic parameters of the class.
    //In other words, it doesn't matter whether a generic method belongs to a generic class or not.
    //Generic methods that can be declared static. Reason: generic parameters are determined when the method is called. Not determined when instantiating a class.
    public static <E>  List<E> copyFromArrayToList(E[] arr){

        ArrayList<E> list = new ArrayList<>();

        for(E e : arr){
            list.add(e);
        }
        return list;

    }
}

The embodiment of generics in inheritance

If B is A subtype (subclass or sub interface) of A and G is A class or interface with generic declaration, G is not A subtype of G!
For example, String is a subclass of Object, but List is not a subclass of List.

The following shows some generic calls on inheritance.

public class GenericTest {

    /*
    1. The embodiment of generics in inheritance

      Although class A is the parent of class B, G < a > and G < b > do not have child parent relationship, and they are juxtaposed.

       Supplement: Class A is the parent of class B, and a < g > is the parent of class B < g >

     */
    @Test
    public void test1(){

        Object obj = null;
        String str = null;
        obj = str;

        Object[] arr1 = null;
        String[] arr2 = null;
        arr1 = arr2;
        //Compilation failed
//        Date date = new Date();
//        str = date;
        List<Object> list1 = null;
        List<String> list2 = new ArrayList<String>();
        //At this time, the types of list1 and list2 do not have a child parent relationship
        //Compilation failed
//        list1 = list2;
        /*
        Counter evidence:
        Assume list1 = list2;
           list1.add(123);Results in mixing non String data. Error.

         */

        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<>();

    }


}

Use of wildcards

Use of wildcards
1. Use type wildcard:?
For example: List <? >, Map<?,?>
List<?> It is the parent class of list, list and other generic lists.
2. Read list <? > It is always safe to use the elements in the list of objects, because no matter what the real type of the list is, it contains objects.
3. It is not allowed to write elements in the list. Because we don't know the element type of c, we can't add objects to it.
 the only exception is null, which is a member of all types.
 adding any element to it is not type safe:
Collection<?> c = new ArrayList();
c.add(new Object()); // Compile time error
Because we don't know the element type of c, we can't add objects to it. The add method has the type parameter E as the element type of the collection. Any parameter we pass to add must be a subclass of an unknown type. Because we don't know what type it 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 we know that it is always an Object.

Bounded Wildcards
 <?>
Allow all generic reference calls
 wildcard specifies the upper limit
Upper bound extensions: when used, the specified type must inherit a class or implement an interface, i.e<=
 wildcard specifies the lower limit
Lower limit super: the type specified when using cannot be less than the class of the operation, that is >=
 examples:

  • <? Extensions Number > (infinitesimal, Number], only reference calls with generics of Number and Number subclasses are allowed
  • <? Super Number > [Number, infinity), only reference calls with generic type Number and Number parent class are allowed
  • <? Extensions compatible > only generics are allowed to be called by reference to the implementation class that implements the compatible interface

The following shows the use and limitations of some wildcards.

  /*
    2. Use of wildcards
       Wildcard:?

       Class A is the parent class of class B. g < a > and G < b > have no relationship. Their common parent class is g <? >


     */

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

        List<?> list = null;

        list = list1;
        list = list2;
        //Compile passed
//        print(list1);
//        print(list2);


        //
        List<String> list3 = new ArrayList<>();
        list3.add("AA");
        list3.add("BB");
        list3.add("CC");
        list = list3;
        //Add (write): for list <? > You can't add data to it.
        //In addition to adding null.
//        list.add("DD");
//        list.add('?');

        list.add(null);

        //Get (read): data can be read. The read data type is 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.Use of restricted wildcards.
        ? extends A:
                G<? extends A> Can be the parent of G < a > and G < b >, where B is the child of A

        ? super A:
                G<? super A> Can be the parent of G < a > and G < b >, where B is the parent of A

     */
    @Test
    public void test4(){
    	//Equivalent to [x < = person
        List<? extends Person> list1 = null;
        //Equivalent to x > = person
        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;

        //Read data:
        list1 = list3;
        Person p = list1.get(0);
        //Compilation failed
        //Student s = list1.get(0);

        list2 = list4;
        Object obj = list2.get(0);
        Compilation failed
//        Person obj = list2.get(0);

        //Write data:
        //Compilation failed
//        list1.add(new Student());

        //Compile passed
        list2.add(new Person());
        list2.add(new Student());

    }

Some parent classes are shown below.

public class Person {
}

Some subclasses and inheritance classes are shown below.

public class Student extends Person {
}

Generic application examples

Here are some examples of generic applications 1.

public class DAO<T> {

    private Map<String,T> map = new HashMap<String,T>();
    //Save objects of type T into Map member variables
    public void save(String id,T entity){
        map.put(id,entity);
    }
    //Get the object corresponding to the id from the map
    public T get(String id){
        return map.get(id);
    }
    //Replace the content in the map where the key is id and change it to an entity object
    public void update(String id,T entity){
        if(map.containsKey(id)){
            map.put(id,entity);
        }
    }
    //Returns all T objects stored in the map
    public List<T> list(){
        //FALSE:
//        Collection<T> values = map.values();
//        return (List<T>) values;
        //correct:
        ArrayList<T> list = new ArrayList<>();
        Collection<T> values = map.values();
        for(T t : values){
            list.add(t);
        }
        return list;

    }
    //Deletes the specified id object
    public void delete(String id){
        map.remove(id);
    }

}

Here are some examples of generic applications 2.

public class User {

    private int id;
    private int age;
    private String name;

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public User(int id, int age, String name) {

        this.id = id;
        this.age = age;
        this.name = name;
    }

    public User() {

    }

    @Override
    public String toString() {
        return "User{" +
                "id=" + id +
                ", age=" + age +
                ", name='" + name + '\'' +
                '}';
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;

        User user = (User) o;

        if (id != user.id) return false;
        if (age != user.age) return false;
        return name != null ? name.equals(user.name) : user.name == null;
    }

    @Override
    public int hashCode() {
        int result = id;
        result = 31 * result + age;
        result = 31 * result + (name != null ? name.hashCode() : 0);
        return result;
    }
}

Here are some examples of generic applications 3.

public class DAOTest {

    public static void main(String[] args) {
        DAO<User> dao = new DAO<User>();

        dao.save("1001",new User(1001,34,"Jay Chou"));
        dao.save("1002",new User(1002,20,"Hannah "));
        dao.save("1003",new User(1003,25,"Cai Yilin"));

        dao.update("1003",new User(1003,30,"Fang Wenshan"));

        dao.delete("1002");

        List<User> list = dao.list();
//        System.out.println(list);
        list.forEach(System.out::println);



    }

}

Topics: Java