1 JAVA Type system
- Let’s take a look at Java’s Type system (class of classes => Type). Type is a common interface for all types (prototype-class, Parameterizedtype, Array type-GenericArrayType, Type variable-typeVariable, and Primitive type-class). Class<T>, described in the previous two reflections and annotations, is an implementation of Type
- ParameterizedType, TypeVariable, GenericArrayType, and WildcardType are subinterface classes
- List
is a generic type. E is a TypeVariable type and List
is a ParameterizedType. The strings in List
are called actual parameter types
- To externalize a type in a generic, use? Extends or? Super for inheritance; Such as
List<? extends Data>
And the inside? Called the WildcardType WildcardType - GenericArrayType Represents an array type of an element whose type is ParameterizedType or TypeVariable, such as T[] or List
[].
- List
- Annotations are new in JDK1.5, and in order to represent annotated elements, the AnnotatedElement type is added. JDK1.8 also has AnnotatedType, which associates Type with the concept of annotated elements.
- AnnotatedType also have four sub interface, and the Type of four subinterface one-to-one correspondence, such as: ParameterizedType Type annotations are parsed into AnnotatedParameterizedType compiler:
@AnTest("list")List<String>list
The concept of generics
- Java generics is a new feature introduced in JDK1.5. Its essence is to parameterize types and solve the problem of uncertain specific object types. The data type it operates on is specified as a type parameter, which can be used in the creation of classes, interfaces, and methods called generic classes, generic interfaces, and generic methods, respectively
Generics: Postpone specifying specific types until object creation or method invocation time
Examples of generic classes and generic methods
- Definition of generic classes
public class MainTest<T> {
private T param;
}
public static void main(String[] args){
MainTest<String> data = new MainTest<String>(){};
ParameterizedType genType1 = (ParameterizedType)data.getClass().getGenericSuperclass(); } Copy the code
- Definition of generic methods
public class MainTest{
public static void main(String[] args){
printData("siting");
}
static <T> T printData(T t){
System.out.println(t); return t; } } Copy the code
- Both interfaces and abstract classes can use generics
4 Type erasure
- When creating instances of generics, the JVM erases the specific type; The bytecode generated by the compilation does not contain the type parameters of the generic type, that is, ArrayList
and ArrayList
are erased as ArrayLists, that is, erased as “native types”, which is a generic erase
public class MainTest {
public static void main(String[] args){
List<String> strArr = new ArrayList<>();
List<Integer> intArr = new ArrayList<>();
Type strClazz = strArr.getClass();
Type intClazz = intArr.getClass(); } } Copy the code
- To see how compiled ByteCode files are represented: idea menu -> View -> show ByteCode
public class MainTest<T> {
T param;
public static void main(String[] args){
MainTest<String> test = new MainTest<>();
test.setParam("siting");
} public T getParam(a) { return param; } public void setParam(T param) { this.param = param; } } Copy the code
public class com/MainTest {
. omit public static main([Ljava/lang/String;)V
L0
LINENUMBER 7 L0 NEW com/MainTest DUP INVOKESPECIAL com/MainTest.<init> (a)V ASTORE 1 L1 LINENUMBER 8 L1 ALOAD 1 LDC "siting" // Call type erased setParam(Object) INVOKEVIRTUAL com/MainTest.setParam (Ljava/lang/Object;)V L2 . omit//getParam returns Object public getParam(a)Ljava/lang/Object; L0 LINENUMBER 10 L0 ALOAD 0 GETFIELD com/MainTest.param : Ljava/lang/Object; ARETURN . omit//setParam takes Object as an input parameter public setParam(Ljava/lang/Object;)V L0 LINENUMBER 11 L0 ALOAD 0 ALOAD 1 PUTFIELD com/MainTest.param : Ljava/lang/Object; RETURN .} Copy the code
- You can see that T(String) is all converted to Object, and the initial initialized String is missing
Inheritance of generics
- A subclass can specify generic parameters for its parent class, which can be a known class (Integer, String, and so on), or it can specify its own generic parameters
- When generics are inherited and the parent generic parameter is specified, the additional generated ParameterizedType is used as the parent of the subclass. If no parent generic parameter is specified, the native type is inherited directly
public class MainTest<T> {
T param;
static public class SubTest1 extends MainTest<String>{}
static public class SubTest2<R> extends MainTest<R>{}
//SubTest3 inherits the primitive type
static public class SubTest3 extends MainTest{} } Copy the code
6 Generic variable TypeVariable
- Define a temporary name, E in Test
as a generic parameter. Generic variables TypeVariable: Generic parameters of a generic type are TypeVariable; When a parent class specifies its own generic parameters using a child class’s generic parameters; Or when A generic attribute is defined in the generic class A
and the generic parameter T of the generic class A
is used, the generic parameter is defined by the compiler as the generic variable TypeVariable instead of being erased
public class MainTest<T> {
List<T> param;
public static void main(String[] args) throws Exception{
Class clazz = MainTest.class;
TypeVariable[] typeVariable = clazz.getTypeParameters();
/ / 1 Field field = clazz.getDeclaredField("param"); ParameterizedType arrayType = (ParameterizedType)field.getGenericType(); // The generic type E of interface List
is instantiated by T, so it is identified as TypeVariable
TypeVariable variable1 = (TypeVariable)arrayType.getActualTypeArguments()[0]; / / 2 ParameterizedType type = (ParameterizedType)SubTest.class.getGenericSuperclass(); TypeVariable variable2 = (TypeVariable)type.getActualTypeArguments()[0]; } static class SubTest<R> extends MainTest<R>{} } Copy the code
7 Parameter type ParameterizedType
public interface ParameterizedType extends Type {
// Get the actual argument, the String in List
; If it is List
, it is TypeVariable
Type[] getActualTypeArguments();
List
-> List
Type getRawType(a);
Type getOwnerType(a); } Copy the code
- Note that we cannot directly obtain the type of a generic that specifies specific parameters, such as
Class clazz = List<String>.class
Not passed at compile time; There is also an object created directly from the generic Class new whose Class is not ParameterizedType, but the Class of the generic itself, as shown in the following example
public class MainTest<T> {
public static void main(String[] args){
MainTest<String> str = new MainTest<String>();
Class variable = str.getClass();
Type genType1 = variable.getGenericSuperclass();
} } Copy the code
- Only parameterized generics are recognized by the compiler as ParameterizedType. There are three ways to get ParameterizedType
// 1 when a subclass inherits a generic type, specify a specific parameter (either a known type such as String or a generic parameter of the subclass)
// 2 Gets the generic attributes defined inside the class, specifying specific generic parameters
// 3 Local code to get the ParameterizedType type from an anonymous internal subclass of the generic with specific generic parameters specified
public class MainTest<T> {
List<T> list;
public static void main(String[] args) throws NoSuchFieldException { SubTest<String> str = new SubTest<>(); / / way Class variable = str.getClass(); // The parent class is the (521)ParameterizedType type ParameterizedType genType = (ParameterizedType)variable.getGenericSuperclass(); The native type of ParameterizedType is (479) class com.mainTest Type clazz = genType.getRawType(); // The native type of maintest. class is (479) class com.maintest Class rawClazz = MainTest.class; // Generic attributes Field field = rawClazz.getDeclaredField("list"); // ParameterizedType is of type (546). ParameterizedType fieldType = (ParameterizedType)field.getGenericType(); 3 / / way MainTest<String> sub3 = new MainTest<String>(){}; // clazz3 is an anonymous subclass Class clazz3 = sub3.getClass(); // The parent class is the (555)ParameterizedType type ParameterizedType genType3 = (ParameterizedType) clazz3.getGenericSuperclass(); The native type of ParameterizedType is (479) class com.mainTest Type type3 = genType3.getRawType(); } public static class SubTest<R> extends MainTest<R>{}} Copy the code
8 WildcardType
Unbounded wildcards: unbounded wildcards? Can be adapted to any reference type:
- When a generic type needs to be passed as a method parameter and its type cannot be determined. Direct use of generic variables without specific generic, easy to cause security risks; If you do type conversions in method code, you can easily get a ClassCastException error
- Why not use Object instead of generic variables? However, there is no inheritance relationship between a generic class and a ParameterizedType. ParameterizedType List
public static void print(List list){}
----->>>
public static void print(List
list){}
Copy the code
- Unbounded wildcards can match any type; But in use? We can’t set a value for a generic class variable because we don’t know what the type is. If you force a new value, subsequent reads are prone to ClassCastException errors. So the compiler limits the ** wildcard? ** generics can only be read, not written
The upper bound bound wildcard
- If you want to receive a List, it can only operate on numeric elements (Float, Integer, Double, Byte, etc.). You can use
List<? Subclass of extends Number >
To indicate that the elements in the List are subclasses of Number
public static void print(List<? extends Number> list) {
Number n = new Double("1.0");
list.add(n);
Number tmp = list.get(0);
}
Copy the code
- As you can see from the picture, there is an upper bound wildcard, because the specific type is uncertain, and it can only be read but not written
The lower bound qualifies the wildcard
class Parent{ }
class Child extends Parent{ }
public class MainTest<T> {
T param;
public static void main(String[] args){
MainTest<? super Child> parent_m = new MainTest<>(); parent_m.setParam(new Child()); Object parent = parent_m.getParam(); } public T getParam() { return param; } public void setParam(T param) { this.param = param; } } Copy the code
- If we define whose parent the wildcard is, then the lower bound defines the wildcard; Such wildcards are readable and writable, and can be converted to any parent class without a ClassCastException.
- Personal guess: Is it because generic downward conversions of wildcards and upper-bound wildcards are prone to ClassCastException errors and up-conversions of lower-bound wildcards are non-classcastException errors, so the Java specification prevents the former from compiling and the latter from compiling?
9 GenericArrayType
public interface GenericArrayType extends Type {
// Get the array element type: A<T> (A<T>[]) or T (T[]) Type getGenericComponentType();
}
Copy the code
- GenericArrayType is a subinterface to ParameterizedType and TypeVariable arrays, such as Test
[][], T[], and GenericArrayType
public class MainTest<T> {
T[] param;
public static void main(String[] args) throws Exception{
Class clazz = MainTest.class;
Field field = clazz.getDeclaredField("param");
GenericArrayType arrayType = (GenericArrayType)field.getGenericType(); TypeVariable variable = (TypeVariable) arrayType.getGenericComponentType(); } } Copy the code
Corrections are welcome
Pay attention to the public account, communicate together, wechat search: sneak forward
- Generics are as simple as that
- Essential foundation for Java programmers: Generic parsing
- Java gets a detailed description of type instances of generics