1. Basic introduction to generics
Generics are equivalent to tags
For example:
In the traditional Chinese medicine store, there are labels on the outside of each drawer
There are many bottles on the supermarket shelves. What is in each bottle and there are labels.
1.1 design background of generics
The collection container class cannot determine what type of objects actually exist in this container in the design phase / declaration phase, so before JDK1.5, the element type can only be designed as Object, and after JDK1.5, generics can be used to solve it. At this time, except the element type is uncertain, other parts are determined, such as how to save and manage the element. Therefore, the element type is designed as a parameter, which is called genericity. Collection, List and ArrayList are type parameters, that is, generics.
The so-called genericity is to allow an identifier to represent the type of an attribute in a class or the return value and parameter type of a method when defining a class or interface. This type parameter will be determined when it is used (for example, inherit or implement this interface, declare variables and create objects with this type ID. it can only be determined when the actual type parameter (type argument) is passed in).
Since JDK1.5, Java has introduced the concept of "parameterized type", which allows us to specify the type of collection elements when creating a collection, such as: List < string >, which indicates that the list can only save 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.
1.2 why generics
So why do we need generics? Can direct objects also store data?
- Solve the security problem of element storage, such as commodity and drug labels.
- To solve the problem of type coercion when obtaining data elements, for example, you don't have to identify goods and drugs every time you get them.
- Java generics can ensure that ClassCastException exceptions will not be generated at runtime if the program does not issue a warning at compile time. At the same time, the code is more concise and robust.
import org.junit.Test; import java.util.ArrayList; public class GenericTest { //Before using generics in a collection: @Test public void test(){ ArrayList list = new ArrayList(); //Requirements: store students' grades list.add(78); list.add(49); list.add(72); list.add(81); list.add(89); //Problem 1: unsafe type // list.add("Tom"); for(Object score : list){ //Problem 2: type conversion exception may occur during forced conversion int stuScore = (Integer)score; System.out.println(stuScore); } } }
2. Use generics in Collections
① The collection interface or collection class is modified to a generic structure in JDK 5.0. (see the source code for this)
② When instantiating a collection class, you can specify the specific generic type, for example: List < string >
③ 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) = > after instantiation: add(String e)
④ 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.
Example
import org.junit.Test; import java.util.*; public class GenericTest { //Using generics in Collections: take ArrayList as an example @Test public void test2(){ ArrayList<Integer> list = new ArrayList<Integer>(); list.add(78); list.add(49); list.add(72); list.add(81); list.add(89); //When compiling, type checking will be carried out to ensure the safety of data // list.add("Tom"); //Mode 1: // for(Integer score :list){ // //The operation of forced rotation 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("Tone",81); map.put("Jack",64); // 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); } } }
Exercise 1
import org.junit.Test; import java.util.Comparator; import java.util.Iterator; import java.util.TreeSet; /** * MyDate Class contains: * private Member variables year,month,day; And define getter and setter methods for each attribute; * */ class MyDate implements Comparable<MyDate> { private int year; private int month; private int day; public int getYear() { return year; } public void setYear(int year) { this.year = year; } public int getMonth() { return month; } public void setMonth(int month) { this.month = month; } public int getDay() { return day; } public void setDay(int day) { this.day = day; } public MyDate() { } public MyDate(int year, int month, int day) { this.year = year; this.month = month; this.day = day; } @Override public String toString() { return "MyDate{" + "year=" + year + ", month=" + month + ", day=" + day + '}'; } //How to write without specifying generics // @Override // public int compareTo(Object o) { // if(o instanceof MyDate){ // MyDate m = (MyDate)o; // // //Comparative year // int minusYear = this.getYear() - m.getYear(); // if(minusYear != 0){ // return minusYear; // } // //Comparison month // int minusMonth = this.getMonth() - m.getMonth(); // if(minusMonth != 0){ // return minusMonth; // } // //Comparison day // return this.getDay() - m.getDay(); // } // // throw new RuntimeException("inconsistent data type passed in!"); // // } @Override public int compareTo(MyDate m) { //Comparative year int minusYear = this.getYear() - m.getYear(); if(minusYear != 0){ return minusYear; } //Comparison month int minusMonth = this.getMonth() - m.getMonth(); if(minusMonth != 0){ return minusMonth; } //Comparison day return this.getDay() - m.getDay(); } } /** * Define an Employee class. * This class contains: private member variables name,age,birthday, * Where birthday is the object of MyDate class; * And define getter and setter methods for each attribute; * And override the toString method to output name, age and birthday * */ class Employee implements Comparable<Employee> { private String name; private int age; private MyDate birthday; public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } public MyDate getBirthday() { return birthday; } public void setBirthday(MyDate birthday) { this.birthday = birthday; } public Employee() { } public Employee(String name, int age, MyDate birthday) { this.name = name; this.age = age; this.birthday = birthday; } @Override public String toString() { return "Employee{" + "name='" + name + '\'' + ", age=" + age + ", birthday=" + birthday + '}'; } //How to write without specifying generics //Sort by name // @Override // public int compareTo(Object o){ // if(o instanceof Employee){ // Employee e = (Employee)o; // return this.name.compareTo(e.name); // } return 0; // throw new RuntimeException("the incoming data type is inconsistent"); // } //Indicates how generics are written @Override public int compareTo(Employee o) { return this.name.compareTo(o.name); } } public class EmployeeTest { //Question 1: use natural sorting @Test public void test(){ TreeSet<Employee> set = new TreeSet<Employee>(); Employee e1 = new Employee("wangxianzhi",41,new MyDate(334,5,4)); Employee e2 = new Employee("simaqian",43,new MyDate(-145,7,12)); Employee e3 = new Employee("yanzhenqin",44,new MyDate(709,5,9)); Employee e4 = new Employee("zhangqian",51,new MyDate(-179,8,12)); Employee e5 = new Employee("quyuan",21,new MyDate(-340,12,4)); set.add(e1); set.add(e2); set.add(e3); set.add(e4); set.add(e5); Iterator<Employee> iterator = set.iterator(); while (iterator.hasNext()){ Employee next = iterator.next(); System.out.println(next); } } //Question 2: sort by birthday date @Test public void test2(){ TreeSet<Employee> set = new TreeSet<>(new Comparator<Employee>() { //Writing after using generics @Override public int compare(Employee o1, Employee o2) { MyDate b1 = o1.getBirthday(); MyDate b2 = o2.getBirthday(); return b1.compareTo(b2); } //Writing before using generics /* @Override public int compare(Object o1, Object o2) { if (o1 instanceof Employee && o2 instanceof Employee) { Employee e1 = (Employee) o1; Employee e2 = (Employee) o2; MyDate b1 = e1.getBirthday(); MyDate b2 = e2.getBirthday(); //Mode 1: //Comparative year int minusYear = b1.getYear() - b2.getYear(); if (minusYear != 0) { return minusYear; } //Comparison month int minusMonth = b1.getMonth() - b2.getMonth(); if (minusMonth != 0) { return minusMonth; } //Comparison day return b1.getDay() - b2.getDay(); //Method 2: directly use compareTo of MyDate return b1.compareTo(b2); } throw new RuntimeException("The data type passed in is inconsistent! "); }*/ }); Employee e1 = new Employee("liudehua",55,new MyDate(1965,5,4)); Employee e2 = new Employee("zhangxueyou",43,new MyDate(1987,5,4)); Employee e3 = new Employee("guofucheng",44,new MyDate(1987,5,9)); Employee e4 = new Employee("liming",51,new MyDate(1954,8,12)); Employee e5 = new Employee("liangzhaowei",21,new MyDate(1978,12,4)); set.add(e1); set.add(e2); set.add(e3); set.add(e4); set.add(e5); Iterator<Employee> iterator = set.iterator(); while (iterator.hasNext()){ System.out.println(iterator.next()); } } }
3. Custom generic structure
3.1 custom generic classes
In actual development and use, we can also customize generic classes to extract common parts as components
Custom generic class: if < T > is declared after the class name, the T symbol can be used as a generic class
public class OrderTest<T> { String orderName; int orderId; //The internal structure of the class can use the generics of the class T orderT; public OrderTest(){ }; public OrderTest(String orderName,int orderId,T orderT){ this.orderName = orderName; this.orderId = orderId; this.orderT = orderT; } @Override public String toString() { return "Order{" + "orderName='" + orderName + '\'' + ", orderId=" + orderId + ", orderT=" + orderT + '}'; } }
3.1.1 inheritance relationship of generic classes
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
If there is no type, it will be erased
Specific type - Subclasses retain the genericity of the parent class: generic subclasses
remove none
Partial retention - Subclasses can be expanded on the basis of parent classes
class Father<T1, T2> {} // Subclasses do not retain the generics of the parent class // 1) If there is no type, it will be erased class Son1 extends Father {// Equivalent to class son extends father < object, Object > {} } //Class son1 < A, b > extends father {/ / two generic types are specified on the basis of father < object, Object > //} // 2) Specific type class Son2 extends Father<Integer, String> {} // Subclasses retain the generics of the parent class // 1) Keep all class Son3<T1, T2> extends Father<T1, T2> {} // 2) Partial retention class Son4<T2> extends Father<Integer, T2> {} // Expand class Son5<T2, T3> extends Father<Integer,T2>{}
Example
import org.junit.Test; //SubOrder: is not a generic class class SubOrder extends OrderTest<Integer> { } //Suborder1 < T >: still generic class class SubOrder1<T> extends OrderTest<T> { } public class GenericTest1 { @Test public void test(){ /** * 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. */ OrderTest order = new OrderTest(); order.setOrderT(123); order.setOrderT("ABC"); //Suggestion: specify the generic type of the class when instantiating OrderTest<String> order1 = new OrderTest<String>("orderAA",1001,"order:AA"); order1.setOrderT("AA:hello"); } @Test public void test2(){ SubOrder sub1 = new SubOrder(); //Since the subclass indicates the generic type when inheriting the parent class with generic type, it is no longer necessary to specify the generic type when instantiating the subclass object. sub1.setOrderT(1122); SubOrder1<String> sub2 = new SubOrder1<>(); sub2.setOrderT("order2..."); } }
The embodiment of generics in inheritance
For example, class A is the parent of class B, but g < a > and G < b > do not have child parent relationship, and they are juxtaposed.
In addition, class A is the parent of class B, and class A
It's B
Parent class of
import org.junit.Test; import java.util.AbstractList; import java.util.ArrayList; import java.util.List; public class GenericTest { @Test public void test(){ // In retrospect, the following are possible Object obj = null; String str = null; obj = str; Object[] arr1 = null; String[] arr2 = null; arr1 = arr2; //Compilation failed // Date date = new Date(); // str = date; //At this time, the types of list1 and list2 do not have a child parent relationship List<Object> list1 = null; List<String> list2 = new ArrayList<String>(); //Compilation failed // list1 = list2; /** * Counter evidence: * Assume list1 = list2; * list1.add(123);This caused an error in mixing non String data. */ show(list1); show2(list2); } public void show2(List<String> list){ } public void show(List<Object> list){ } @Test public void test2(){ // The following is the parent-child relationship AbstractList<String> list1 = null; List<String> list2 = null; ArrayList<String> list3 = null; list1 = list3; list2 = list3; List<String> list4 = new ArrayList<>(); } }
3.1.2 considerations for customizing generic classes / interfaces
be careful:
- A generic class may have multiple parameters. In this case, multiple parameters should be put together in angle brackets. For example: < E1, E2, E3 >
- Constructor of generic class is consistent with that of ordinary class: public GenericClass() {}. Error example: public genericclass < T > () {}
- After instantiation, the structure of the original generic location must be consistent with the specified generic type.
- Instance references of different generics cannot be assigned to each other.
- 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. - If the generic structure is an interface or abstract class, you cannot create an object of the generic class.
- Simplified operation of JDK1.7 generics: ArrayList flist = new ArrayList < > ();
- Basic data types cannot be used in the specification of generic types, but can be replaced by wrapper classes.
- 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, class generics cannot be used in static methods (because static methods are loaded earlier than instances, static methods can only use generic methods).
- Exception class cannot be generic
Example
class OrderTest<T> { String orderName; int orderId; T orderT; public OrderTest(){ //The compilation fails because the generic type is just a placeholder and there is no real class information, so there is no way to new (the compiler will erase the type information) // T[] arr = new T[10]; //Compile passed T[] arr = (T[]) new Object[10]; }; // 2. The constructor of generic class is consistent with that of ordinary class: ` public GenericClass() {} '. Error example: ` public genericclass < T > () {}` public OrderTest(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 + '}'; } //9. Class generics cannot be used in static methods. // public static void show(T orderT){ // System.out.println(orderT); // } public void show(){ //10. Exception class cannot be generic // try{ // // // }catch(T t){ // // } } } import org.junit.Test; import java.util.ArrayList; public class GenericTest1 { @Test public void test3(){ ArrayList<String> list1 = null; ArrayList<Integer> list2 = new ArrayList<Integer>(); //4. References with different generics cannot be assigned to each other. //list1 = list2; Person p1 = null; Person p2 = null; p1 = p2; } }
3.2 custom 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.
Generic method: the generic structure appears in the method, and the generic parameters have nothing to do with the generic parameters of the class. (that is, the generic parameter of a generic method may not be the generic parameter of a class)
Generic methods that can be declared static. Reason: generic parameters are determined when the method is called. Not determined when instantiating a class.
Format:
[Access rights] <generic paradigm> Return type method name([Generic identity parameter name]) Exception thrown For example: public static <E> List<E> copyFromArrayToList(E[] arr) throws Exception{ }
class OrderTest<T> { String orderName; int orderId; //The internal structure of the class can use the generics of the class T orderT; public OrderTest(){ }; public OrderTest(String orderName,int orderId,T orderT){ this.orderName = orderName; this.orderId = orderId; this.orderT = orderT; } // Static generic method, customized E as generic parameter public static <E> List<E> copyFromArrayToList(E[] arr){ ArrayList<E> list = new ArrayList<>(); for(E e : arr){ list.add(e); } return list; } // Non static generic method public <K> List<K> Array2List(K[] arr) { ArrayList<K> list = new ArrayList<>(); for(K e : arr){ list.add(e); } return list; } //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 + '}'; } } import org.junit.Test; import java.util.ArrayList; import java.util.List; public class GenericTest1 { //Test generic methods @Test public void test4(){ OrderTest<String> order = new OrderTest<>(); 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); } }
Using generic methods in non generic classes
import java.util.ArrayList; import java.util.List; public class SubOrder extends OrderTest<Integer>{ //SubOrder: is not a generic class public static <E> List<E> copyFromArrayToList(E[] arr){//Static generic method ArrayList<E> list = new ArrayList<>(); for(E e : arr){ list.add(e); } return list; } }
4. Usage scenarios of generics
As mentioned above, common parts can be extracted for reuse. For example, during CRUD operations on databases, basic methods can be encapsulated into generic classes and provided to different table operations
import java.util.List; public class DAO<T> { //DAO for common operations of tables //Add a record public void add(T t){ } //Delete a record public boolean remove(int index){ return false; } //Modify a record public void update(int index,T t){ } //Query a record public T getIndex(int index){ return null; } //Query multiple records public List<T> getForList(int index){ return null; } //generic method //Example: how many records are there in the acquisition table? Get the maximum employee induction time? public <E> E getValue(){ return null; } }
class Customer { //This class corresponds to the customers table in the database } class Student { } class StudentDAO extends DAO<Student> {//You can only operate on the DAO of a table } class CustomerDAO extends DAO<Customer>{//You can only operate on the DAO of a table } import org.junit.Test; import java.util.List; public class DAOTest { @Test public void test(){ CustomerDAO dao1 = new CustomerDAO(); dao1.add(new Customer()); List<Customer> list = dao1.getForList(10); StudentDAO dao2 = new StudentDAO(); Student student = dao2.getIndex(1); } }
5. Use of wildcards
Basic introduction
Wildcard:?
For example: List <? >, Map<?,?>
List<?> It is the parent class of list and various generic lists. 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.
be careful:
- Add any element to the list except null <? > To which is not type safe
- Cannot be used on a generic declaration of a generic method
Error example: public <? > void test(ArrayList<?> list) {}
Correct example: public < T > void test (ArrayList <? > list) {} - Cannot be used on declarations of generic classes
- Cannot be used on instantiation creation of objects
public static void main(String[] args) { List<Object> list1 = null; List<String> list2 = null; List<?> list = null; // Because you used it? Wildcard, so it is the parent class of list1 and list2 list = list1; list = list2; final ArrayList<?> arrayList = new ArrayList<>(); // Compile time error because we don't know the element type of c, we can't add objects to it. // The add method takes 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. arrayList.add(new Object()); // Allow to join arrayList.add(null); final Object o = arrayList.get(0); }
5.1 use of restricted wildcards
<?> Allow all generic reference calls
However, we can specify the upper and lower limits for wildcards
- Wildcard to specify the upper limit:
Extensions: when used, the specified type must inherit a class or implement an interface, i.e<= - Wildcard to specify lower limit
super: the type specified when using cannot be less than the class of the operation, i.e. >=
give an example:
- < T extends Number > (infinitesimal, Number]
Type T must be a subclass of Number - < T super number > [number, infinity)
Type T must be a parent of Number - <T extends Comparable>
Type T must implement the Comparable interface - <T extends Comparable<? super T>>
Type t must implement the Comparable interface, and the type of this interface is t or any parent class of T (the sizes of instances of T and instances of T's parent class can be compared with each other)
6. Use examples
interface Info{ // Only subclasses of this interface represent human information } class Contact implements Info{ // Indicates contact information private String address ; // Contact address private String telephone ; // contact information private String zipcode ; // Postal Code public Contact(String address,String telephone,String zipcode){ this.address = address; this.telephone = telephone; this.zipcode = zipcode; } public void setAddress(String address){ this.address = address ; } public void setTelephone(String telephone){ this.telephone = telephone ; } public void setZipcode(String zipcode){ this.zipcode = zipcode; } public String getAddress(){ return this.address ; } public String getTelephone(){ return this.telephone ; } public String getZipcode(){ return this.zipcode; } @Override public String toString() { return "Contact [address=" + address + ", telephone=" + telephone + ", zipcode=" + zipcode + "]"; } } class Introduction implements Info{ private String name ; // full name private String sex ; // Gender private int age ; // Age public Introduction(String name,String sex,int age){ this.name = name; this.sex = sex; this.age = age; } public void setName(String name){ this.name = name ; } public void setSex(String sex){ this.sex = sex ; } public void setAge(int age){ this.age = age ; } public String getName(){ return this.name ; } public String getSex(){ return this.sex ; } public int getAge(){ return this.age ; } @Override public String toString() { return "Introduction [name=" + name + ", sex=" + sex + ", age=" + age + "]"; } } class Person<T extends Info>{ private T info ; public Person(T info){ // Setting information property content through constructor this.info = info; } public void setInfo(T info){ this.info = info ; } public T getInfo(){ return info ; } @Override public String toString() { return "Person [info=" + info + "]"; } } public class GenericPerson{ public static void main(String args[]){ Person<Contact> per = null ; // Declaring a Person object per = new Person<Contact>(new Contact("Beijing","01088888888","102206")) ; System.out.println(per); Person<Introduction> per2 = null ; // Declaring a Person object per2 = new Person<Introduction>(new Introduction("Li Lei","male",24)); System.out.println(per2) ; } }
Exercise 1
import java.util.*; 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(){ //Wrong: the parent type should not be forcibly converted to a child type, which is prone to error // 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); } } 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; } } import java.util.List; /** * Create objects of DAO class and call save, get, update, list and delete respectively * Method to manipulate the User object, and use the Junit unit test class to test. */ 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); } }
EDG awesome!!
Last | General catalogue | Next |
---|---|---|
13, Assemble | Java basic directory | Unfinished to be continued |