1. The concept of generics
Generics, or parameterized types. That is, the type is parameterized from an original concrete type, similar to a variable parameter in a method, in which the type is also defined as a parameter (type parameter), and then passed in the concrete type (type argument) when the/is used.
- JDK1.5 will be introduced later.
- Make your code more generic and flexible.
- The core goal is to address the issue of compile-time security checks for container types.
In the use of generics, the data type of an operation is specified as a parameter. This parameter type can be used in classes, interfaces, and methods, known as generic classes, generic interfaces, and generic methods, respectively.
2. Use of generics
Type parameters (type variables) are used as placeholders, and may have one or more, for the entire class. By convention the type argument is a single uppercase letter:
- E: element, generally used in linear structure sets
- K: key, generally used to represent keys in a set of key value types
- V: value, typically used to represent values in a collection of key value types
- N: digital
- T: type
2.1. Generic classes/interfaces
Class Class name < type parameter 1, type parameter 2... > {}
Interface Interface name < Type parameter 1, type parameter 2... > {}
/ / a generic class
public class HashMap<K.V> extends AbstractMap<K.V> implements Map<K.V>, Cloneable.Serializable {}
// Generic interface
public interface Map<K.V> {}
Copy the code
2.2. Generic methods
Type parameter 0 (return value type) Method name < Type parameter 1 Parameter 1, type parameter 2 Parameter 2... > {}
public final V setValue(V newValue) {
V oldValue = value;
value = newValue;
return oldValue;
}
Copy the code
Note: You cannot use generic type parameters in static content such as static methods, static primitives, etc.
Public class A<T> {public static void func(T T) {Copy the code
2.3. The wildcard
Question mark wildcard? Indicates an unknown type. Wildcards can be used for parameters, fields, local variables, and return types. Can be approximated as generics of generics.
-
Wildcard matching generic types can only be read, not written. Because we don’t know what type of data the container holds, we can only read it, not add it.
-
It is best not to use wildcards in return types, because it is safer to know exactly what type a method returns.
public static void main(String[] args) {
List<String> name = new ArrayList<String>();
List<Integer> age = new ArrayList<Integer>();
List<Number> number = new ArrayList<Number>();
name.add("icon");
age.add(18);
number.add(314);
getData(name); // icon
getData(age); / / 18
getData(number); / / 314
}
// List
is the logical parent of List
, List
, and all List< concrete type arguments >.
public static void getData(List
data) {
System.out.println("data :" + data.get(0));
}
Copy the code
2.4. Upper and lower boundary constraints
- Upper bound qualified wildcards:
<? extends E>
, indicating that only type E and its subtypes are accepted. - Lower bound qualified wildcards:
<? super E>
, indicating that only type E and its parent type are accepted.
public static void main(String[] args) {
List<String> name = new ArrayList<String>();
List<Integer> age = new ArrayList<Integer>();
List<Number> number = new ArrayList<Number>();
name.add("icon");
age.add(18);
number.add(314);
getUperNumber(name); String is not a subclass of Number
getUperNumber(age); / / 18
getUperNumber(number); / / 314
}
public static void getData(List
data) {
System.out.println("data :" + data.get(0));
}
// List
, which is logically the parent of classes List
, List
, etc.
public static void getUperNumber(List<? extends Number> data) {
System.out.println("data :" + data.get(0));
}
Copy the code
3. How generics work
Generics in the Java language are pseudo-generics implemented by erase. generics information (type variables, parameterized types) is completely removed after compilation.
3.1. Type erasure
The generic information exists only before compilation, and the compiled bytecode does not contain the type information in the generic. Therefore, the compiler removes type arguments at compile time, called type erasure.
Types such as List
and List
become lists when compiled. All the JVM sees is the List, and generic information is not visible to the JVM.
Class c1 = new ArrayList<String>().getClass();
Class c2 = new ArrayList<Integer>().getClass();
System.out.println(c1 == c2); // true
Copy the code
3.1.1. Generic types that do not specify an upper bound are treated asObject
Type substitution
// Generic class Java source code
public class Test<T> {
private T test;
}
// The compiled code
public class Test{
public Test(a){}
private Object test; // The original generic T is replaced with Object
}
Copy the code
3.1.2. Generic types with upper bounds are replaced by upper bounds
// Generic class Java source code
public class Test<T extends String> {
private T num;
}
// The compiled code
public class Test{
public Test(a){}
private String test; // The original generic type T is replaced by the upper bound type String
}
Copy the code
3.2. Bypass compile time generic type checking
List<Integer> list = new ArrayList<>();
list.add(123); // Normal compilation
list.add("string"); Java.lang. String cannot be converted to java.lang.Integer.
Copy the code
Based on type erasure, we can use reflection to get around this limitation.
List<Integer> list = new ArrayList<>();
list.add(123);
try {
// Since there is no upper bound on the generic parameters in List, the add method can add any subtype parameters of Object
Method method = list.getClass().getDeclaredMethod("add", Object.class);
method.invoke(list, "string");
method.invoke(list, true);
method.invoke(list, 45.6);
} catch (Exception e) {
e.printStackTrace();
}
Copy the code