Generics
Limit elements in a collection to a specific type.
the term
A few notes:
Parameterized types and primitive types are compatible with each other
ArrayList collection1 = new ArrayList<Integer>();//Pass, no warningArrayList<Integer> collection2 = new ArrayList();//Pass, there is warning
Parameterized types do not consider inheritance relationships of type parameters
ArrayList<String> collection3 = new ArrayList<Object>();//Compilation does not pass ArrayList<Object> collection4 = new ArrayList<String>();//Compilation does not pass
but
ArrayList collection5 = new ArrayList<Integer>();ArrayList<String> collection6 = collection5;//Compiled by
"?" Wildcard
"?" means any type. Use the "?" wildcard character to refer to various parameterized types. It can call methods that are not related to parameterization (such as size() method), and cannot call methods that are related to parameterization (such as add() method)
Wildcard extension
Qualify the upper boundary of wildcard characters
ArrayList<? extends Number > collection1= new ArrayList<Integer >();//Compile by ArrayList<? extends Number > collection2= new ArrayList<String>();//Compile by not
Qualify the lower boundary of wildcard characters
ArrayList<? super Integer > collection3= new ArrayList<Number >();//Compile by ArrayList<? super Integer > collection4= new ArrayList<String>();//Compile by not
Custom generic methods
C++ template functions
template <class T> T add(T x, T y){ return (T)(x+y);}Java generics are basically implemented entirely in the compiler, used by the compiler to perform type checks and type judgments, and then generate ordinary non-generic bytecode. This implementation technique is "erasure".
"Erase" instance
Generics are provided to the javac compiler to define the input type of the collection. When the compiler compiles a collection with type description, the "type" information will be removed.
public class GenericTest { public static void main(String[] args) { new GenericTest().testType(); } public void testType(){ ArrayList<Integer> collection1 = new ArrayList<Integer>(); ArrayList<String> collection2= new ArrayList<String>(); System.out.println(collection1.getClass()==collection2.getClass()); //The class types of the two are the same, that is, the bytecode is the same System.out.println(collection2.getClass().getName()); //The class is java.util.ArrayList, and there is no actual type parameter information}}Output
true
java.util.ArrayList
Use reflection to skip the compiler and add other types of data to a generic collection.
Only reference types can be used as actual parameters for generic methods:
public class GenericTest { public static void main(String[] args) { swap(new String[]{"111","222"},0,1);//Compile by //swap(new int[]{1,2},0,1); //Compile by not compiling because int is not the reference type swap(new Integer[]{1,2},0,1);//Compile by } /*Swap the i-th and jth elements of array a*/ public static <T> void swap(T[]a,int i,int j){ T temp = a[i]; a[i] = a[j]; a[j] = temp; }}But note that basic types can sometimes be used as actual parameters, because there are automatic packing and unboxing. Example (compiled and passed):
public class GenericTest { public static void main(String[] args) { new GenericTest().testType(); int a = biggerOne(3,5); //int and double, get the interchange as Number Number b = biggerOne(3,5.5); //String and int get the interchange as Object Object c = biggerOne("1",2); } //Return y from x,y public static <T> T biggerOne(T x,T y){ return y; }}At the same time, this example also shows that when the actual parameters are inconsistent, T takes the intersection, that is, the first common parent class. In addition, if you use Number b = biggerOne(3,5.5); to String c = biggerOne(3,5.5); then the compilation error is reported:
Error:(17, 29) java: Incompatible types: Inferred types do not meet the upper limit inference: java.lang.Number&java.lang.Comparable<? extends java.lang.Number&java.lang.Comparable<?>>
Upper limit: java.lang.String,java.lang.Object
But there is one thing I didn't understand. I step-by-step debugging in IDEA and found the result is as follows: Generic debugging screenshot-1 I don't know why b is of type Double (but it will compile and report an error when receiving the return value on Double b). I don’t know if it has anything to do with the IDE. Does the IDE display the most accurate type of this object when debugging?
Type inference of type parameters
The process by which the compiler judges the actual type parameters of a generic method is called type inference.
When a certain type variable is applied only one of all parameters and return values in the entire parameter list, it is determined based on the actual application type at that time when the method is called. That is, the type of generic parameters is directly determined based on the parameter type or return value passed when the method is called. For example:
swap(new String[3],1,2) -> static <E> void swap(E[]a,int i,int j)
When a type variable is applied in multiple places in all parameters and return values of the entire parameter list, if so many actual application types correspond to the same type when calling the method, the type of the generic parameter is that type. For example:
add(3,5) -> static <T> T add(T a,T b)
When a certain type variable is applied in many places in all parameters and return values of the entire parameter list, if the actual application types in so many places correspond to different types when calling the method and there is no return value, the maximum intersection type among the multiple parameters, that is, the first common parent class. For example:
fill(new Integer[3],3.5) -> static <T> void fill(T a[],T v)
The actual corresponding type of this example is Number, compiled and run problems.
When a type variable is applied in multiple places in all parameters and return values of the entire parameter list, if the actual application types in so many places correspond to different types when calling the method, and there is a return value, the type of the return value is given priority, for example:
int x = add(3,3.5) -> static <T> T add(T a,T b)
The above example compiled error, and the x type is changed to float also reported an error, and the change to Number is successful.
Examples of type inference of parameter types are transitive:
copy(new Integer[5],new String[5]) -> static <T> void copy(T []a,T []b)
This example infers that the actual parameter type is Object and compiled through.
copy(new ArrayList<String>,new Integer[5]) -> static <T> void copy(Collection<T>a,T[]b)
This example directly determines the type variable as String type based on the parameterized ArrayList class instance, and reports an error in the compilation.
Custom generic classes
example
public class GenericDao<T>{ public void add(T x){ } public T findById(int id){ return null; } public void delete(T obj){ } public void delete(int id){ } public void update(T obj){ } public T findByUserName(String name){ return null; } public <T> Set<T> findByConditions(String where){ return null; }}Note: When a variable is declared as a generic, it can only be called by instance variables and methods (and embedded types), but not by static variables and static methods. Because static members are shared by parameterized classes, static members should not have class-level type parameters.
Comparison of generic methods and generic classes
example:
public class A<T>(){ //Member method of generic class, the T is restricted by T following A public T memberFunc(){ return null; } //Generic method, here T and T are different from T of class A public static <T> T genericFunc(T a){ return null; } public static void main(String[] args) { //Compiled without passing //Integer i = A<String>().findByUserName("s"); //Compiled by Set<Integer> set= A<String>().findByConditions("s"); }}Here Integer i = A<String>().findByUserName("s"); will compile and report an error:
Error:(35, 61) java: Incompatible type: java.lang.String cannot be converted to java.lang.Integer
From this example, it can be seen that the T of the generic method and the T of the class A are different.
Generics and Reflections
Obtain the actual type parameters of a generic through reflection
Use generic variables as parameters of the method, and use the getGenericParameterTypes method of the Method class to obtain the actual type parameter example of the generic:
public class GenericTest { public static void main(String[] args) throws Exception { getParamType(); } /*Use reflection to obtain the actual parameter type of method parameters*/ public static void getParamType() throws NoSuchMethodException{ Method method = GenericTest.class.getMethod("applyMap",Map.class); //Get the type of generic parameters of method Type[] types = method.getGenericParameterTypes(); System.out.println(types[0]); // Parameterized type ParameterizedType pType = (ParameterizedType)types[0]; // Primitive type System.out.println(pType.getRawType()); // Actual type parameter System.out.println(pType.getActualTypeArguments()[0]); System.out.println(pType.getActualTypeArguments()[1]); } /* Method for testing parameter types*/ public static void applyMap(Map<Integer,String> map){ }}Output result:
java.util.Map<java.lang.Integer, java.lang.String>interface java.util.Mapclass java.lang.Integerclass java.lang.String