Java generic, java generic
I. generic definition
1. What is java generics?
Generics are new features of Java SE 1.5. The nature of generics is parameterized, that is, the Data Type operated is specified as a parameter.
This type of parameter can be used to create classes, interfaces, and methods, which are called generic classes, generic interfaces, and generic methods.
2. Why do I need generics?
The advantage of introducing generics in Java is that it is secure and simple. You can advance running errors to compilation errors.
Before Java SE 1.5, if there is no generics, the parameter "arbitrary" can be implemented by referencing the type Object ", the disadvantage of "arbitrary" is explicit forced type conversion,
This type of conversion requires developers to make predictions about the actual parameter type. If the forced type conversion is incorrect, the compiler may not prompt an error and it will only appear during running.
Exception, which is a security risk. The advantage of generics is to check the type security during compilation, and all the forced conversions are both automatic and implicit, improving the code reuse rate.
3. What are the benefits of generics?
(1) type security.
By knowing the type restrictions of variables defined by generics, the compiler can improve the type security of Java programs more effectively.
(2) eliminate forced type conversion.
Removes many forced type conversions from source code. This makes the code more readable and reduces the chance of errors. All mandatory conversions are both automatic and implicit.
(3) improve performance.
?
4. Precautions for generic usage
(1) Generic variables cannot be basic data types.
For example, there is no ArrayList <double>, and only ArrayList <Double>. After the data type is erased, replace the type variable (T) in the original class of ArrayList with the Object,
However, the double value cannot be stored for the Object type.
(2) There can be multiple generic type parameters separated by commas.
Node <T, E, V, K>
(3) You cannot use instanceof for the exact generic type. The following operations are invalid because errors occur during compilation.
If (arrayList instanceof ArrayList <String>) {}// compilation Error
(4) You cannot create an exact Generic Array.
For example, List <String> [] ls = new ArrayList <String> [10]; // an error is reported during compilation.
List <String> [] list = new ArrayList [10]; // correct
List <?> [] Ls = new ArrayList <?> [10]; // correct
(5) Problems of generics in static methods and static classes
Static methods and static variables in generic classes cannot use the generic parameters declared by generic classes.
Public/* static */class StaticGenerator <T> {
// Generic static method
Public static <T> void show (T t ){
System. out. println ("generic test: t is" + t );
}
/* Public static T one; // compilation Error
Public static T print (T one) {// compilation Error
Return null;
}*/
}
Because the instantiation of generic parameters in generic classes is specified when defining generic objects (such as ArrayList <Integer>, static variables and static methods do not need to be called using objects.
The object is not created. It is of course incorrect to determine the type of the generic parameter. However, pay attention to the following situations:
Generic method: The T used in the generic method is the T defined in the method, rather than the T in the generic class.
(6) Transfer of generic type references
In Java, the following form of reference transfer is not allowed:
ArrayList <String> arrayList1 = new ArrayList <Object> (); // compilation Error
ArrayList <Object> arrayList2 = new ArrayList <String> (); // compilation Error
II. Implementation principle of generics
1. Type Erasure
ArrayList <String> arrayString = new ArrayList <String> ();
ArrayList <Integer> arrayInteger = new ArrayList <Integer> ();
System. out. println (arrayString. getClass () = arrayInteger. getClass (); // output: true
During compilation, all generic information is erased. The List <Integer> and List <String> types are converted to the List type (original type) after compilation ).
Generics in Java are basically implemented at the compiler level, which is also why Java generics are called "pseudo generics.
2. Original Type
The original type is the real type in bytecode after the generic type information is erased. Whenever a generic type is defined, the corresponding original type is automatically provided.
The name of the original type is the class name of the generic type after the type parameter is deleted. Erase the type variable and replace it with the limited type (T is an infinitely fixed type variable, replace it with an Object ).
// Generic type
Class Pair <T> {
Private T value;
Public T getValue (){
Return value;
}
Public void setValue (T value ){
This. value = value;
}
}
// Original Type
Class Pair {
Private Object value;
Public Object getValue (){
Return value;
}
Public void setValue (Object value ){
This. value = value;
}
}
In Pair <T>, T is an infinitely fixed type variable, so it is replaced with an Object. If it is a Pair <T extends Number>, the type variable is replaced by the Number type after the erased operation.
3. Bridge Method
Node <T>: declare the Node <String> or Node <Integer>. During the runtime, the JVM is regarded as Node <Object>.
Cause: For generic code, the Java compiler actually secretly helps us implement a Bridge method ).
Solution: Node <T extends Comparable <T> replaces Node <T>
Public class Node <T> {
Public T data;
Public Node (T data) {this. data = data ;}
Public void setData (T data ){
System. out. println ("Node. setData ");
This. data = data;
}
}
Public class MyNode extends Node <Integer> {
Public MyNode (Integer data) {super (data );}
Public void setData (Integer data ){
System. out. println ("MyNode. setData ");
Super. setData (data );
}
}
The test code is as follows:
MyNode mn = new MyNode (5 );
Node n = mn; // A raw type-compiler throws an unchecked warning
N. setData ("Hello"); // Causes a ClassCastException to be thrown.
Integer x = mn. data;
System. out. println (n. toString ());
System. out. println (x );
The compiled code is as follows:
Class MyNode extends Node {
// Bridge method generated by the compiler
Public void setData (Object data ){
SetData (Integer) data );
}
Public void setData (Integer data ){
System. out. println ("MyNode. setData ");
Super. setData (data );
}
//...
}
Iii. Generic usage
1. Generic Type
Generic types are used in the definition of classes and are called generic classes. You can use generics to open the same interface to a group of operations. The most typical types are container classes, such as List, Set, and Map.
The most basic syntax for generic classes:
Class name <generic ID: You can enter any ID number to identify the specified generic type> {
Private generic ID/* (member variable type) */var;
.....
}
One of the most common generic classes:
// T can be written as any identifier here. Common parameters such as T, E, K, and V are usually used to represent generic parameters.
// When instantiating a generic class, you must specify the specific type of T.
Public class Generic <T> {
// The type of the member variable "key" is T, and the type of "T" is specified externally.
Private T key;
Public Generic (T key) {// The type of the parameter key in the Generic constructor is also T, and the type of T is specified by the external
This. key = key;
}
Public T getKey () {// The type of the returned value of the generic method getKey is T, and the type of T is specified externally
Return key;
}
}
2. Generic Interfaces
The definitions and usage of generic interfaces and generic classes are basically the same. Generic interfaces are often used in various types of producers. Let's look at an example:
// Define a generic interface
Public interface Generator <T> {
Public T next ();
}
/**
* When no generic arguments are passed in, they are the same as those defined in the generic class. When declaring a class, you must add the generic declaration to the class together.
* Class FruitGenerator <T> implements Generator <T> {
* If the generic type is not declared, for example, class FruitGenerator implements Generator <T>, the compiler reports an error: "Unknown class"
*/
Class FruitGenerator <T> implements Generator <T> {
@ Override
Public T next (){
Return null;
}
}
/**
* When passing in a wildcard real parameter:
* Define a producer to implement this interface, although we have created only one generic interface Generator <T>
* However, We can input countless real parameters for T to form countless types of Generator interfaces.
* When implementing a class to implement a generic interface, if you have passed the generic type into the real parameter type, replace all the places that use the generic type with the passed real parameter type.
* That Is, T in Generator <T>, public T next (); must be replaced with the input String type.
*/
Public class FruitGenerator implements Generator <String> {
Private String [] fruits = new String [] {"Apple", "Banana", "Pear "};
@ Override
Public String next (){
Random rand = new Random ();
Return fruits [rand. nextInt (3)];
}
}
3. Generic Method
1> when defining a generic method, you must add a <T> in front of the returned value to declare that this is a generic method, holding a generic T, then, you can use generic T as the return value of the method.
The function of Class <T> is to specify the specific type of the generic type. The Class <T> type variable c can be used to create the object of the generic Class.
/**
* Generic Method
* Created by zhongtian465 on 2017-10-12.
*/
Public class GenericMethod {
/**
* Generic Method
* @ Param clazz: used to create a generic object and Class <T>: Specifies the specific type of generic T.
* @ Param <T> declares that this method is a generic method and has a type T.
* @ Return T the return type is T.
* @ Throws IllegalAccessException
* @ Throws InstantiationException
*/
Public <T> T getObject (Class <T> clazz) throws IllegalAccessException, InstantiationException {
T object = clazz. newInstance ();
Return object;
}
Public static void main (String [] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException {
GenericMethod genericMethod = new GenericMethod ();
User user = (User) genericMethod. getObject (Class. forName ("com. zt. study. java. generic. User "));
System. out. println (user );
}
}
Why should we use generic methods?
Because generic classes need to specify the type during instantiation, if you want to change the type, you have to re-create it once, it may not be flexible enough; the generic method can specify the type when calling, which is more flexible.
2> the generic method contains multiple generic examples.
Public class GenericMore <T, K> extends Generic <T> {
Private K k;
Public GenericMore (T key ){
Super (key );
}
/**
* The generic method contains multiple generic examples.
* @ Param container
* @ Param <T>
* @ Param <V>
* @ Return
*/
Public <T, V> V getValue (Generic <T> container ){
T key = container. getKey ();
System. out. println ("container key:" + key );
Map <T, V> map = new HashMap <> ();
V vaule = map. get (key );
Return vaule;
}
Public static void main (String [] args ){
GenericMore m = new GenericMore ("name ");
String value = (String) m. getValue (new Generic ("key "));
System. out. println (value );
}
}
3> generic methods and variable parameters
Public <T> void printMsg (T... args ){
For (T t: args ){
System. out. println ("generic test: t is" + t );
}
}
4> static generic Method
/**
* If you define a generic static method in a class, you need to add additional generic declarations (define this method as a generic method)
* It is not allowed to use the declared generics in the generic class for static methods.
* For example, public static void show (T t) {..}. The compiler will prompt the error message:
"StaticGenerator cannot be refrenced from static context"
*/
Public static <T> void show (T t ){
System. out. println ("generic test: t is" + t );
}
4. Generic instances
Public class GenericTest {
// This class is a generic class, which has been introduced above
Public class Generic <T> {
Private T key;
Public Generic (T key ){
This. key = key;
}
// What I want to talk about is actually this. Although generics are used in methods, this is not a generic method.
// This is only a common member method in the class, but its return value is to declare the generics that have been declared in the generic class.
// You can continue to use the generic T in this method.
Public T getKey (){
Return key;
}
/**
* This method is obviously faulty. In the compiler, we will be prompted with this error message "cannot reslove symbol E"
* Because generic E is not declared in the class declaration, the compiler will not recognize it when E is used as the form parameter and return value type.
Public E setKey (E key ){
This. key = keu
}
*/
}
/**
* This is a real generic method.
* The <T> between public and return values is indispensable. This indicates that this is a generic method and a generic T is declared.
* This T can appear anywhere in the generic method.
* The number of generic types can be any number
* For example, public <T, K> K showKeyName (Generic <T> container ){
*...
*}
*/
Public <T> T showKeyName (Generic <T> container ){
System. out. println ("container key:" + container. getKey ());
// Of course, this example is not suitable to illustrate the characteristics of generic methods.
T test = container. getKey ();
Return test;
}
// This is not a Generic method. This is a common method. It only uses the Generic class Generic <Number> as the form parameter.
Public void showKeyValue1 (Generic <Number> obj ){
System. out. println ("generic test: key value is" + obj. getKey ());
}
// This is not a generic method. It is also a common method, but it only uses wildcard wildcards?
// At the same time, this also confirms what is described in the wildcard chapter ,? Is a type of real parameter. It can be seen as the parent class of all classes such as Number.
Public void showKeyValue2 (Generic <?> Obj ){
System. out. println ("generic test: key value is" + obj. getKey ());
}
/**
* This method is faulty. the compiler will prompt the error message "UnKnown class 'E '"
* Although we declare <T>, it also indicates that this is a generic method that can process generics.
* But only the generic type T is declared and the generic type E is not declared. Therefore, the compiler does not know how to handle the E type.
Public <T> T showKeyName (Generic <E> container ){
...
}
*/
/**
* This method is also problematic. The compiler will prompt the error message "UnKnown class 'T '"
* For the compiler, T is not declared in the project. Therefore, compilation does not know how to compile the class.
* This is not a correct generic method declaration.
Public void showkey (T genericObj ){
}
*/
}
4. wildcard characters
1> upper limit of wildcard characters
Use the extends keyword to specify that this type must inherit a class, or implement an interface, or this class or interface itself. For example, List <? Extends Number>
2> lower bound of a wildcard
Use the extends keyword to set the lower limit of the type by ensuring that the type must be the parent class of T. For example, List <? Super Integer>
3> unbounded wildcards
You can create a separate "?" without any modification. "?" Can represent any type, "any" is also an unknown type. For example, List <?>
4> wildcard and unrestricted wildcard
The specified wildcard character specifies the type. There are two types of qualified wildcard, one is <? Extends T> it sets the upper limit of the type by ensuring that the type must be a subclass of T,
The other is <? Super T> it sets the lower bound of the type by ensuring that the type must be the parent class of T. Generic types must be initialized with a limited number of types. Otherwise, compilation errors may occur.
<?> It indicates a non-qualified wildcard, because <?> Can be replaced by any type.
5> Use Cases of wildcards
Usage principles: producer-extends, consumer-super.
I) extends is used to retrieve data from generic classes;
Ii) when writing data to a generic class, use super;
Iii) do not use wildcards (both extends and super) to retrieve and write data ).
Example:
// Wildcard type for parameter that serves as an E producer
Public void pushAll (Iterable <? Extends E> src ){
For (E e: src)
Push (e );
}
Assume that there is an object Stack of the instantiated stack <Number>, and src has Iterable <Integer> and Iterable <Float>;
The type mismatch Error occurs when the pushAll method is called, because the generics in Java are immutable,
Neither Iterable <Integer> nor Iterable <Float> is a child type of Iterable <Number>.
// Wildcard type for parameter that serves as an E consumer
Public void popAll (Collection <? Super E> dst ){
While (! IsEmpty ())
Dst. add (pop ());
}
Assume that there is an Object Stack of the instantiated stack <Number>, dst is Collection <Object>;
The type mismatch Error occurs when you call the popAll method, because Collection <Object> is not a child type of Collection <Number>.
Java. util. Collections's copy method (JDK1.7) perfectly interprets:
Public static <T> void copy (List <? Super T> dest, List <? Extends T> src ){
Int srcSize = src. size ();
If (srcSize> dest. size ())
Throw new IndexOutOfBoundsException ("Source does not fit in dest ");
If (srcSize <COPY_THRESHOLD |
(Src instanceof RandomAccess & dest instanceof RandomAccess )){
For (int I = 0; I <srcSize; I ++)
Dest. set (I, src. get (I ));
} Else {
ListIterator <? Super T> di = dest. listIterator ();
ListIterator <? Extends T> si = src. listIterator ();
For (int I = 0; I <srcSize; I ++ ){
Di. next ();
Di. set (si. next ());
}
}
}
Iv. inverter and covariant
1. Liskov replacement Principle (Liskov Substitution Principle, LSP ):
All objects that reference the base class (parent class) must be transparently used.
LSP contains the following four meanings:
1> subclass has a method of the parent class, and the specific subclass must implement the abstract method of the parent class.
2> Add methods to subclass.
3> when the subclass overwrites or implements the parent class method, the method parameters are looser than those of the parent class method.
4> when the subclass overwrites or implements the parent class method, the return value of the method is stricter than that of the parent class.
2. Definition of inverter and covariant:
Inverter and covariant are used to describe the inheritance relationship after type transformation. Their definitions are as follows:
If expression A and expression B represent the type, expression f (Direction) represents the type conversion, and value ≤ represents the inheritance relationship (for example, A ≤ BA ≤ B indicates that A is A subclass derived from B );
F (frequency) is A contravariant. When A ≤ BA ≤ B, f (B) ≤ f (B) f (A) ≤ f () established;
F (variance) is covariant. When A ≤ BA ≤ B, f (A) ≤ f (B) f () ≤ f (B) was established;
F (random) is invariant. When A ≤ BA ≤ B, neither of the above two formulas is true, that is, f () there is no inheritance relationship with f (B.
3. Generic inverter and covariant
If f (A) = ArrayList <A> is f (frequency) inverter, covariant, or unchanged?
If it is an inverter, The ArrayList <Integer> is the parent type of the ArrayList <Number>;
If it is a covariant, The ArrayList <Integer> is a child type of the ArrayList <Number>;
If they do not change, the two do not inherit from each other.
Example:
1> unchanged
ArrayList <Number> list = new ArrayList <Integer> (); // type mismatch
List <? Extends Number> list2 = new ArrayList <Number> ();
List2.add (new Integer (1); // compilation Error
List2.add (new Float (1.2f); // compilation Error
The description type is unchanged.
2> covariant
List <? Extends Number> list3 = new ArrayList <Integer> ();
Integer is a subclass of Number: <? Extends> implements generic covariant
3> Inverter
List <? Super Number> list4 = new ArrayList <Object> ();
Object is the parent class of Number: <? Super> implements generic inverters