generic paradigm
The specific type is specified only when creating an object or calling a method
advantage
-
The essence of generics is to parameterize types, that is, to control the types of formal parameters by specifying different types through generics without creating new types. Improve code reusability
-
The introduction of generics improves security. Generics provides a compile time type security detection mechanism, which can detect illegal types at the compile stage
-
In the absence of generics, to implement arbitrary parameters, use "Object". The disadvantage of this is that explicit forced type conversion is required every time. This conversion needs to be carried out when the developer can predict the actual parameter types. If the forced type conversion is wrong, it can only be found at runtime, which is itself a security risk.
Summary: the benefit of generics is that type safety can be checked at compile time, and all cast types are automatic and implicit.
Generic class
Generic types are used in the definition of classes and are called generic classes. Through generics, you can complete the operation of a group of classes and open the same interface to the outside world. The most typical are various container classes, such as List, Set and Map.
When instantiating a generic class, you can pass in generic type arguments to limit its types. If it is not passed in, the type defined by the method or member variable using generics in the generic class can be any type.
generic interface
The definition and use of generic interfaces and generic classes are basically the same. Generic interfaces are often used in various kinds of producers
generic method
Generic class indicates the specific type of the generic when instantiating the class; Generic method refers to the specific type of generic when calling the method.
< T > between public and return value is very important, which can be understood as declaring this method as a generic method.
Only methods that declare < T > are generic methods. Member methods that use generics in generic classes are not generic methods< T> The following T represents the return value of the method.
public <T> T genericMethod(T a) { return a; }
Generic methods in generic classes
class GenerateTest<T> { /** * 1,Generic methods are used, not generic methods * 2,The generic type passed in after the class is instantiated is the formal parameter type of the method */ public void show_1(T t) { System.out.println(t.toString()); } /** * 1,A generic method is declared in a generic class, using generic E, which can be of any type. The type can be the same as T or different. * 2,Since generic methods declare generic < E > when declared, the compiler can correctly recognize even if no generic is declared in the generic class Generics identified in generic methods. * 3,When this method is called, any type of argument can be passed in, which is not limited by the < T > generic argument type passed in during instantiation */ public <E> void show_3(E t) { System.out.println(t.toString()); } }
Variable parameter types in methods can also be generic.
Static methods and generics
Static methods in a class use generics: static methods cannot access generics defined on the class; If the reference data type of static method operation is uncertain, the generic type must be defined on the method.
That is, if static methods want to use generics, static methods must also be defined as generic methods.
Generics of classes cannot be used in static methods.
1. If you define a static method that uses generics in a class, you need to add an additional generic declaration (define this method as a generic method) even if the static method uses generics already declared in the generic class. For example: public static void show(T t) {...}, the compiler will prompt an error message: "staticgenerator cannot be referred from static context"
2. 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.
3. Generic methods that can be declared static. Reason: generic parameters are determined when the method is called. It is not determined when the class is initialized, so it doesn't matter
Details of generic 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 >
Generic wildcard
Common generic wildcards
Convention: to improve readability
- ? Represents an ambiguous java type
- T (Type) represents a specific java type
- K V (Key Value) respectively represents the Key Value in the java Key Value
- E (element) stands for Element
'?' Unbounded wildcards
Basic Usage
package keyAndDifficultPoints.Generic; import java.util.ArrayList; import java.util.List; public class Test_Wildcard_Character { public static void main(String[] args) { List<Dog> dogList = new ArrayList<>(); test(dogList); test1(dogList); } static void test(List<? extends Animal> animals) { System.out.println("test output:"); for (Animal animal : animals) { System.out.print(animal.toString() + "-"); } } static void test1(List<Animal> animals) { System.out.println("test1 output:"); for (Animal animal : animals) { System.out.print(animal.toString() + "-"); } } } class Animal { @Override public String toString() { return "Animal"; } } class Dog extends Animal { @Override public String toString() { return "Dog"; } }
For types that are uncertain or do not care about the actual operation, you can use infinite wildcards (a question mark in angle brackets, i.e. <? >), indicating that you can hold any type. For example, the test() method defines the upper bound, but does not care about the specific type. Therefore, all subclasses of the incoming Animal can be supported without error, while test1() cannot.
Upper and lower bounds of extends and super
- extends determines the upper bound format: <? Extensions E > indicates that the parameter may be e or a subclass of E. The upper bound is e
- If the type passed in is not e or a subclass of E, the compilation will not succeed
- You can use the method of E in generics, otherwise you have to convert to e to use it
- Super determines the lower bound format: <? super E>. Using super in type parameters means that the parameters in this generic type must be e or the parent class of E. The lower bound is e
? And t both represent uncertain types. The difference is that we can operate on T, but for? no way,
T is a definite type, which is usually used for the definition of generic classes and generic methods,? Is an uncertain type, which is usually used for calling code and formal parameters of generic methods, and cannot be used to define classes and generic methods.
? Difference between and T
- T can ensure the consistency of generic parameters,
- T can be multi qualified through &, such as public static < T extensions list & Collection > void test2 (T) {}
- ? You can use superclass qualification instead of T
- T there is only one type qualification method T extends A
- ? There are two types of qualification: extends and super.
Generic principle (generic erasure)
Type Erasure
Java generics are pseudo generics. During compilation, all generic information will be erased, which is often called generic erasure.
Generics in Java are basically implemented at the compiler level. The generated Java bytecode does not contain the type information in generics. The type parameters added when using generics are removed by the compiler when compiling. This process is called type erasure.
For example, List < Object > and List < string > defined in the code will become List after compilation, and the JVM only sees List. The type information attached by generics is not visible to the JVM. The java compiler will find possible errors as much as possible during compilation, but it still cannot avoid type conversion exceptions at runtime. Type erasure is also an important difference between the generic implementation of Java and the implementation of C + + template mechanism.
Type the original type retained after erasure
The primitive type is the real type of the type variable in the bytecode after erasing the generic information. Whenever a generic type is defined, the corresponding primitive type is automatically provided. The type variable is erased and its qualified type is used (the indefinite variable is replaced by Object).
Note: before running, generics specify multiple limits, and take the minimum range or common subclass during compilation.
Generic method call
When calling generic methods, you can specify generics or not. Without specifying a generic type, the type of a generic variable is the lowest level of the same parent class of several types in the method until Object. When specifying a generic type, several types in the method must be the generic instance type or its subclasses.
class Test { public static void main(String[] args) { //When no generics are specified int a1 = add(1, 2); //Both parameters are integers, so T is of type Integer Number b1 = add(1, 1.2);//One of these two parameters is Integer and the other is Float, so the lowest level of the same parent class is Number Object c1 = add(1, "my");//One of these two parameters is Integer and the other is Float, so the lowest level of the same parent class is Object //When specifying generics int a = Test.<Integer>add(1, 2);//Integer is specified, so it can only be integer type or its subclass // int b = Test.< Integer>add(1, 2.2);// Compilation error, integer specified, cannot be Float Number c = Test.<Number>add(1, 2.2); //It is specified as Number, so it can be Integer and Float } //This is a simple generic method public static <T> T add(T x, T y) { return x; } }
Compile time type safety detection
ArrayList<String> arrayList = new ArrayList<String>(); arrayList.add(1); //Compilation error ArrayList<String> arrayList1 = new ArrayList(); //First case arrayList1.add(1); //Compilation error ArrayList arrayList2 = new ArrayList<String>();//The second case arrayList2.add(1);
The use of generic variables will be checked before compilation.
Type checking is for a reference. If a reference is used to call a generic method, the method called by the reference will be type checked, regardless of the object it really refers to.
The conflict between type erasure and polymorphism and its solution
public class Test_principle05 { public static void main(String[] args) { } } class Generic<T> { //key the type of this member variable is t, and the type of T is specified externally private T var; public T getVar() { return var; } public void setVar(T var) { this.var = var; } } class MyGeneric extends Generic<Integer>{ @Override public Integer getVar() { return super.getVar(); } @Override public void setVar(Integer var) { super.setVar(var); } }
After the Generic type is erased, the parent class Generic will become as follows
class Generic { private Object var; public Object getVar() { return var; } public void setVar(Object var) { this.var = var; } }
The subclass MyGeneric remains unchanged. In order to compare with the parent class after generic erasure, the subclass is also pasted below
class MyGeneric extends Generic<Integer>{ @Override public Integer getVar() { return super.getVar(); } @Override public void setVar(Integer var) { super.setVar(var); } }
Through comparison, we can find that the setVar method should be overloaded rather than overridden
Rewriting and overloading
Overload: first, it is located in a class or its subclass and has the same method name, but the method parameters are different, and the return value types can be the same or different.
override: it generally represents the relationship between subclasses and parent classes. Its main characteristics are: the method names are the same, the parameters are the same, but the specific implementation is different.
However, through verification, we can know that it is indeed rewriting, not overloading
public static void main(String[] args) { MyGeneric myGeneric = new MyGeneric(); myGeneric.setVar(new Integer(1)); myGeneric.setVar(new Object());//Compilation error }
If it is overloaded, the fourth line of code will not report an error, because it calls different overloaded methods. However, it is found that the compilation report is wrong, that is, there is no overloaded function whose parameter is Object. Therefore, it is rewritten, so that MyGeneric objects can only call their own rewritten methods.
The reason is that the JVM uses the bridge method to implement such rewriting.
By looking at the bytecode file, we intended to override the subclasses of setValue and getValue methods. There are actually four methods. The last two methods are the bridge methods generated by the compiler itself. You can see that the parameter types of the bridge methods are all Object, that is, the two bridge methods that we can't see in the subclass really cover the two methods of the parent class. The @ Oveerride on our own defined setValue and getValue methods is just an illusion. The internal implementation of the bridge method is just to call the two methods we rewrite ourselves.
Therefore, the virtual machine skillfully uses clever methods to solve the conflict between type erasure and polymorphism.
A generic type cannot be a base data type
You cannot replace a base type with a type parameter. For example, there is no ArrayList < double >, only ArrayList < double >. Because when the type is erased, the original type of ArrayList becomes Object, but the Object type cannot store the double value and can only reference the double value.
instanceOf of collection at compile time
ArrayList<String> arrayList = new ArrayList<String>();
After the type is erased, only the original type is left in ArrayList < String > and the generic information String does not exist.
Then, it is wrong to use the following method for type query at compile time
if( arrayList instanceof ArrayList<String>)
Covariance, inversion and invariance
Inversion and covariance are used to describe the inheritance relationship after type transformation. Its definition: if A and B represent types, f(⋅) represents type conversion, and ≤ represents inheritance relationship (for example, A ≤ B represents that A is A subclass derived from b);
- f(⋅) is A contravariant. When A ≤ B, f(B) ≤ f(A) is true;
- f(⋅) is covariant. When A ≤ B, f(A) ≤ f(B) holds;
- f(⋅) is invariant. When A ≤ B, the above two formulas are not tenable, that is, f(A) and f(B) have no inheritance relationship with each other.
Three characteristics of common type conversions
generic paradigm
Let f (a) = ArrayList < a >, then when f(⋅), is the inverse, covariant or constant? If it is an inverse, ArrayList < integer > is the parent type of ArrayList < number >; If it is covariant, ArrayList < integer > is a subtype of ArrayList < number >; If it is unchanged, there is no mutual inheritance between them. The error in instantiating the list object with ArrayList < integer > in the opening code indicates that the generic type is unchanged.
array
Let f(A)=[]A, it is easy to prove that the array is covariant:
Number[] numbers = new Integer[3];
method
Call method result = method(n); According to the Liskov Substitution Principle, the type of the incoming parameter n should be the subtype of the method parameter, that is, typeof(n) ≤ typeof(method's parameter); Result should be the base type of the returned value of method, i.e. typeof(methods's return) ≤ typeof(result)
Generics implement covariance and inversion
- <? Extensions > implements the covariance of generic types, such as:
List<? extends Number> list = new ArrayList<Integer>();
- <? Super > implements the inversion of generic types, such as:
List<? super Number> list = new ArrayList<Object>();
Small summary
- To get data from a generic class, use extends;
- To write data to a generic class, use super;
- If you want to get and write, you don't need wildcards (that is, neither extends nor super).