Phase to recommend
- (3) Detailed explanation of HashMap
- (1) Detailed explanation of ArrayList
- (2) A detailed description of the LinkedList
- Java Collections Framework
- Thread pool details
- Introduction of synchronized
- ThreadLocal, you got it?
- Java Exception Mechanism
- New JDK8 feature Stream
- Classmate, are you familiar with volatile?
- MySQL index
What is a generic
Generics are parameterized types, which treat the type they operate on as a parameter.
Why do we need generics
-
Before generics, we could create a collection that stored objects of type Object. This collection could store objects of any data type (except for the eight basic types). Without generics, we would need to specify the data type of each element stored in the collection. It’s easy to have a ClassCastException ClassCastException in your program.
-
With generics, we can specify the type of data to store when we create a collection. For example, when we create an ArrayList that can only store String data, when we add Integer data to the collection, the compilation will fail, ensuring the security of the collection type, and eliminating the cast of the type when fetching the collection data.
The meanings of T, E, K and V
These are essentially wildcards, no difference, just a code convention. For example, in the above code, T can be replaced by any letter between A and Z without affecting the normal operation of the program. However, if T is replaced by another letter, the readability may be weaker. Normally, T, E, K, V,? This is the convention:
- ? Represents an indeterminate Java type
- T (type) represents a specific Java type
- K and V respectively represent key values in Java key-value pairs
- E (Element) stands for element
A generic class
Class is the most basic element in object oriented. When we design a class, we need to consider not only the attributes and methods of the class, but also the extensibility of the class. Generics provide us with the implementation to improve the extensibility of the class. With generics, we can operate on a set of classes and open up the same interface to the public, and by passing in different data types, we can operate on the data of that type.
Definition of a generic class
[access modifier] class class name < generic identifier > {}
- Examples of generic classes:
/** * @author GJY * @date 2021/7/18 22:22 * @version 1.0 * T /** * @author GJY * @date 2021/7/18 22:22 * @version 1.0 * T {GenericClass<T> {GenericClass<T> {GenericClass<T> {GenericClass<T> {GenericClass<T> {private T name; public T getName() { return name; } public void setName(T name) { this.name = name; } public static void main(String[] args) { GenericClass<String> demo1 = new GenericClass<>(); Demo1. setName(" Programmer who does not drink milk tea "); System.out.println(demo1.getName()); GenericClass<Integer> demo2 = new GenericClass<>(); demo2.setName(212); System.out.println(demo2.getName()); }}Copy the code
Generic classes derive subclasses
-
If a subclass that inherits from a parent (generic class) is a generic class, then the generic type of the subclass and the parent must be the same.
class children<T> extends GenericParent<T> { }
-
If a subclass that inherits from a parent (generic class) is a non-generic class, the generic type of the parent must be explicitly specified when the subclass declares the inherited parent.
class children extends GenenricParent<String> { }
Generic method
A generic class is a concrete type that we specify when we instantiate the class, and a generic method is a concrete type that we specify when we call a method. A generic method enables a method to vary independently of the class.
Definition of generic methods
[access modifier] <T,E... Return value type method name (parameter list) {}
Pay attention to
- Only <T,E.. The > generic identifier identifies the method as a generic method, as in the generic class above
public T getName() { return name; }
It’s not a generic method.
Examples of generic methods:
Public class GenericClass<T> {public class GenericClass<T> {public class GenericClass<T> {public class GenericClass<T> { Public static <T> void genericMethod(T birthday){system.out.println (birthday); Public static <T,V,E> void genericMethod2(T T,V V,E E){system.out.println (T +"-"+ V +"-"+ E); } public static <T> void GenericVariadic(T... t){ for (int i = 0; i < t.length; i++) { System.out.println(t[i]); } } public static void main(String[] args) { GenericClass.genericMethod(new Date()); GenericClass.genericMethod("1999-12-31"); / / call more generic types of generic methods GenericClass. GenericMethod2 (" the Programmer do not drink milk tea ", 212110); / / generic variable parameter GenericClass. GenericVariadic (1, 2, 3, 4, 5, "4"); }}Copy the code
A generic interface
Definition of a generic interface
Interface Interface name < generic id. Generic id.... > {generic identifier method name (); }Copy the code
-
Definition of the corresponding implementation class that implements the generic interface:
Class Implements interface name [< generic id >] < generic id > {}
Pay attention to
-
If the implementation class that implements the generic interface is not a generic class, the generic type of the generic interface must be explicitly specified when declaring the implementation of the generic interface.
class interfaceImpl implements GenericInterface<String> { }
-
If the implementation class that implements the generic interface is a generic class, then the generic type of the implementation class must contain the generic type of the interface’s generic class.
Class interfaceImpl<T, other generic identifier... > implements GenericInterface<T> { }
Examples of generic interfaces:
Public interface GenericInterface<T> {T method(T e); }Copy the code
- The implementation class is non-generic
/** * @author GJY * @date 2021/7/18 22:46 * @version 1.0 * Implementation class is not generic, */ public Class GenericInterfaceImpl Implements GenericInterface<String> {@override public String method(String e) { return e; } public static void main(String[] args) { GenericInterfaceImpl gis = new GenericInterfaceImpl(); Gis. Method (" No milk tea Programmer"); }}Copy the code
- The implementation classes are generic classes
/** * @author GJY * @date 2021/7/18 22:46 * @version 1.0 Public class GenericInterfaceImpl<T,V> implements GenericInterface<T> {T name; public class GenericInterfaceImpl<T,V> implements GenericInterface<T> {T name; V id; public GenericInterfaceImpl(T name, V id) { this.name = name; this.id = id; } @Override public T method(T e) { return e; } public static void main(String[] args) { GenericInterfaceImpl<String, Integer> gis = new GenericInterfaceImpl<>(" non-teacup Programmer",212); Gis. Method (" No milk tea Programmer"); }}Copy the code
Generic wildcard
Generic wildcards are usually used with “?” Replace the specific type argument.
Problem is introduced into
We all know that Interger is a subclass of Number, so in a method that takes a parameter Generic
, can we pass in an instance of Generic
? Let’s test it out:
- First we define a Generic class
public class Generic<T> { T element; public T getElement() { return element; } public void setElement(T element) { this.element = element; } @Override public String toString() { return element +""; }}Copy the code
- Declare an passed parameter of
Generic<Number>
And tests passed in when the method is calledGeneric <Intege>
Type argument
public class GenericTest { public static void methodTest(Generic<Number> gn){ System.out.println(gn); } public static void main(String[] args) { Generic<Integer> gi = new Generic<>(); gi.setElement(212); GenericTest.methodTest(gi); / / error}Copy the code
Obviously, when we declare the parameter asGeneric<Number>
Type, passed in when the method is calledArgument to type Generic<Integer>
, even though Integer is a subclass of Number.
Infinite wildcard
- In this case, the infinite wildcard
"?"
That’s where it comes in. Use itGeneric<? >
To replace the parameter in the method definitionGeneric<Number>
, you can pass in any type of data when the method is called. The modification can be performed normally as follows:
public class GenericTest { public static void methodTest(Generic<? > gn){ System.out.println(gn); } public static void main(String[] args) { Generic<Integer> gi = new Generic<>(); Generic<String> gs = new Generic<>(); gi.setElement(212); Gs. setElement(" Programmer who does not drink milk tea "); GenericTest.methodTest(gi); GenericTest.methodTest(gs); }}Copy the code
Upper wildcard <? extends T>
Definition of an upper bound wildcard
Class/interface <? Extends argument type >
Requires that the type of the generic type be either the argument type or a subclass of the argument type
- With an upper bound wildcard, we use it to solve the above problem, especially for parent-child inheritance relationships (the parent of Integer is Number). Therefore, we can set the argument type in the upper bound wildcard to type Number, so that the generic type (i.e. As long as it’s either Number itself or a subclass of Number. Use the following:
public class GenericTest { public static void methodTest(Generic<? extends Number> gn){ System.out.println(gn); } public static void main(String[] args) {// Generic<Integer> gi = new Generic<>(); gi.setElement(212); GenericTest.methodTest(gi); }}Copy the code
Lower limit wildcard <? super T>
The lower bound wildcard requires that the type of the generic type be either the argument type or a parent of the argument type
The definition of the lower limit wildcard
Class/interface <? Super argument type >
- The following is an example:
Public class GenericTest {// This method can only take arguments from the parent class of the Generic type Number or Number. super Number> gn){ System.out.println(gn); } public static void main(String[] args) { Generic<String> gs = new Generic<>(); Generic<Object> gi = new Generic<>(); Generic<Number> gn = new Generic<>(); gi.setElement(212); GenericTest.methodTest(gs); / / error GenericTest. MethodTest (gi); //Object is the parent class of Number, compiled by genericTest.methodTest (gn); // The argument is of type Number and is compiled through}}Copy the code
Generic erasure
Generics were introduced in JDK1.5. Before that, there were no generics. However, generics code is perfectly compatible with previous code, so since information about generics only exists in the compiler of the code, generic-related information is erased before it enters the JVM.
Just beginning
By running the following code, we can see that intList and StrList, the two ArrayList collections we defined for String and Integer types, are the same ArrayList. That’s because the type variables we passed in at run time, String and Integer, are erased.
public static void main(String[] args) { ArrayList<Integer> intList = new ArrayList<>(); ArrayList<String> StrList = new ArrayList<>(); System.out.println(intList.getClass().getSimpleName()); //ArrayList System.out.println(StrList.getClass().getSimpleName()); //ArrayList System.out.println(intList.getClass() == StrList.getClass()); //true }Copy the code
Unrestricted erasure
Unlimited erasure, which means whatever generic type you give me, the Java virtual machine will erase your type and make it Object, right
- When we instantiate a Generic class of type String, will its member variables still be of type String after compilation? Let’s use reflection to find out.
public class Generic<T> { T element; public T getElement() { return element; } public void setElement(T element) { this.element = element; } @Override public String toString() { return element +""; } public static void main(String[] args) { Generic<String> gs = new Generic<>(); // Use reflection to retrieve the Generic Class object Class<? extends Generic> cs = gs.getClass(); For (Field declaredField: Cs.getdeclaredfields ()) {// Output: element:class java.lang.Object System.out.println(declaredField.getName()+":"+declaredField.getType()); }}}Copy the code
- We were surprised to see that the element member variable was erased to type Object, so whatever the data type of the generic class we defined, the Java virtual machine would erase it to type Object.
Restricted erasure
After compilation, it will be erased to the upper bound type
public class Generic<T extends Number> { T element; public T getElement() { return element; } public void setElement(T element) { this.element = element; } @Override public String toString() { return element +""; } public static void main(String[] args) { Generic<Integer> gs = new Generic<>(); // Use reflection to retrieve the Generic Class object Class<? extends Generic> cs = gs.getClass(); For (Field declaredField: Cs.getdeclaredfields ()) {// Output: element:class java.lang.Number System.out.println(declaredField.getName()+":"+declaredField.getType()); }}}Copy the code
Erases the parameters of a type definition in a method
The same generic erasure can be scoped in generic methods
public class Generic<T extends Number> { T element; Public <T extends Number> T method(T value){return value; } public T getElement() { return element; } public void setElement(T element) { this.element = element; } @Override public String toString() { return element +""; } public static void main(String[] args) { Generic<Integer> gs = new Generic<>(); // Use reflection to retrieve the Generic Class object Class<? extends Generic> cs = gs.getClass(); // Retrieve all member methods of the Generic class. For (Method declaredMethod: Cs.getdeclaredmethods ()) {//if(declaredmethod.getName ()=="method") // Print the name of the method and the type of the return value System.out.println(declaredMethod.getName() + ":" + declaredMethod.getReturnType()); }}}Copy the code
- In the example above, we defined a
The Generic class of type Integer
, the compiled member methodThe return value types of method() and getElement() are erased to the upper limit of the generics Number
.
The bridge method
InfoImpl implements the Info interface and specifies that the generic type of the interface is Integer and overwrites the method() method in the interface. We use reflection to see what methods are in the compiled InfoImpl implementation class and what types of return values are available.
public interface Info<T> {
T method(T t);
}
Copy the code
public class InfoImpl implements Info<Integer> { @Override public Integer method(Integer i) { return i; } public static void main(String[] args) {// Get the bytecode Class of InfoImpl <InfoImpl> infoClass = InfoImp.class; / / get all the Method [] mes. = infoClass getDeclaredMethods (); For (Method Method: mes) {// output the name of the Method in the implementation class and the return value type if(method.getName()! ="main") System.out.println(method.getName() + ":" + method.getReturnType()); }}} The program outputs: method:class java.lang.Integer Method :class java.lang.objectCopy the code
After running the main method above, we can see that two method methods with the same name appear in the InfoImpl implementation class, but with different types of return values and argument lists. Why is this, when only one method is overridden in the implementation class? The Java virtual machine adds a method() with a return value of type Object and a parameter type of type Object. This method is a bridge method. Since our generic interface Info of type Integer will be erased to Object when compiled, our implementation class InfoImpl will ensure our specification and constraints on the implementation of the interface. So the Java virtual machine will help us generate a method() in the implementation class with a return value type and a parameter type of Object, thus preserving the implementation relationship between the interface and the class.
Generics and arrays
Creating a generic array has the following specifications:
- You can declare generic array references, such as
ArrayList<String> [] listArray;
, but you cannot directly create array objects with generics, such asArrayList<String> [] listArray = new ArrayList<String>[2];
Therefore, we can create a generic array by:
// Create a generic array ArrayList<Integer> [] arrayLists = new ArrayList[5]; ArrayList ArrayList<Integer> list = new ArrayList<>(); list.add(212); ArrayLists [0]=list; arrayLists[0]=list; // Get Integer I = arrayLists[0].get(0); System.out.println(i); / / 212Copy the code
That’s a summary of generics in Java. Mastering the use of generics can increase the reuse rate of our code and improve the security of our programs by eliminating cast practices.