Java类型系统

Type
Type是所有类型的父接口, 如原始类型(raw types,对应Class)、 参数化类型(parameterized types, 对应ParameterizedType)、 数组类型(array types,对应GenericArrayType)、 类型变量(type variables, 对应TypeVariable)和基本(原生)类型(primitive types, 对应Class), 子接口有ParameterizedType, TypeVariable, GenericArrayType, WildcardType, 实现类有Class
ParameterizedType
- Type getRawType(): 返回承载该泛型信息的对象, 如上面那个Map承载范型信息的对象是Map
- Type[] getActualTypeArguments(): 返回实际泛型类型列表, 如Map<String,String>实际范型列表中有两个元素, 都是String
- Type getOwnerType(): 返回是谁的member.(上面那两个最常用)
TypeVariable
类型变量, 范型信息在编译时会被转换为一个特定的类型, 而TypeVariable就是用来反映在JVM编译该泛型前的信息. 它的声明是这样的: public interface TypeVariable extends Type 也就是说它跟GenericDeclaration有一定的联系, 我是这么理解的: TypeVariable是指在GenericDeclaration中声明的、这些东西中的那个变量T、C; 它有如下方法:
- Type[] getBounds(): 获取类型变量的上边界, 若未明确声明上边界则默认为Object
- D getGenericDeclaration(): 获取声明该类型变量实体
- String getName(): 获取在源码中定义时的名字
注意:
- 类型变量在定义的时候只能使用extends进行(多)边界限定, 不能用super;
- 为什么边界是一个数组? 因为类型变量可以通过&进行多个上边界限定,因此上边界有多个
GenericArrayType
范型数组,组成数组的元素中有范型则实现了该接口; 它的组成元素是ParameterizedType或TypeVariable类型,它只有一个方法:
- Type getGenericComponentType(): 返回数组的组成对象, 即被JVM编译后实际的对象
WildcardType
该接口表示通配符泛型, 比如? extends Number 和 ? super Integer 它有如下方法:
- Type[] getUpperBounds(): 获取范型变量的上界
- Type[] getLowerBounds(): 获取范型变量的下界
注意:
- 现阶段通配符只接受一个上边界或下边界
如何在反射中运用Type
运行期能够获取到具体类型的方式
import java.lang.reflect.TypeVariable;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class Generic<T> {
public Map<String, Teacher> map = new HashMap<>();
public void setList(List<Number> k) {
}
public List<String> getList() {
return null;
}
public void set(T t) {
ArrayList<String> strings = new ArrayList<>();
TypeVariable<? extends Class<? extends ArrayList>>[] parameters = strings.getClass().getTypeParameters();
System.out.println(parameters[0].getName());
}
public <T extends Number> void setWithBound(T t) {
}
public T get() {
return null;
}
}
它反编译后的代码
{
public java.util.Map<java.lang.String, Teacher> map;
descriptor: Ljava/util/Map;
flags: (0x0001) ACC_PUBLIC
Signature: #48 // Ljava/util/Map<Ljava/lang/String;LTeacher;>;
public Generic();
descriptor: ()V
flags: (0x0001) ACC_PUBLIC
Code:
stack=3, locals=1, args_size=1
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: aload_0
5: new #7 // class java/util/HashMap
8: dup
9: invokespecial #9 // Method java/util/HashMap."<init>":()V
12: putfield #10 // Field map:Ljava/util/Map;
15: return
LineNumberTable:
line 7: 0
line 10: 4
LocalVariableTable:
Start Length Slot Name Signature
0 16 0 this LGeneric;
LocalVariableTypeTable:
Start Length Slot Name Signature
0 16 0 this LGeneric<TT;>;
public void setList(java.util.List<java.lang.Number>);
descriptor: (Ljava/util/List;)V
flags: (0x0001) ACC_PUBLIC
Code:
stack=0, locals=2, args_size=2
0: return
LineNumberTable:
line 15: 0
LocalVariableTable:
Start Length Slot Name Signature
0 1 0 this LGeneric;
0 1 1 k Ljava/util/List;
LocalVariableTypeTable:
Start Length Slot Name Signature
0 1 0 this LGeneric<TT;>;
0 1 1 k Ljava/util/List<Ljava/lang/Number;>;
Signature: #61 // (Ljava/util/List<Ljava/lang/Number;>;)V
public java.util.List<java.lang.String> getList();
descriptor: ()Ljava/util/List;
flags: (0x0001) ACC_PUBLIC
Code:
stack=1, locals=1, args_size=1
0: aconst_null
1: areturn
LineNumberTable:
line 18: 0
LocalVariableTable:
Start Length Slot Name Signature
0 2 0 this LGeneric;
LocalVariableTypeTable:
Start Length Slot Name Signature
0 2 0 this LGeneric<TT;>;
Signature: #64 // ()Ljava/util/List<Ljava/lang/String;>;
public void set(T);
descriptor: (Ljava/lang/Object;)V
flags: (0x0001) ACC_PUBLIC
Code:
stack=3, locals=4, args_size=2
0: new #16 // class java/util/ArrayList
3: dup
4: invokespecial #18 // Method java/util/ArrayList."<init>":()V
7: astore_2
8: aload_2
9: invokevirtual #19 // Method java/lang/Object.getClass:()Ljava/lang/Class;
12: invokevirtual #23 // Method java/lang/Class.getTypeParameters:()[Ljava/lang/reflect/TypeVariable;
15: astore_3
16: getstatic #29 // Field java/lang/System.out:Ljava/io/PrintStream;
19: aload_3
20: iconst_0
21: aaload
22: invokeinterface #35, 1 // InterfaceMethod java/lang/reflect/TypeVariable.getName:()Ljava/lang/String;
27: invokevirtual #41 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
30: return
LineNumberTable:
line 22: 0
line 23: 8
line 24: 16
line 25: 30
LocalVariableTable:
Start Length Slot Name Signature
0 31 0 this LGeneric;
0 31 1 t Ljava/lang/Object;
8 23 2 strings Ljava/util/ArrayList;
16 15 3 parameters [Ljava/lang/reflect/TypeVariable;
LocalVariableTypeTable:
Start Length Slot Name Signature
0 31 0 this LGeneric<TT;>;
0 31 1 t TT;
8 23 2 strings Ljava/util/ArrayList<Ljava/lang/String;>;
16 15 3 parameters [Ljava/lang/reflect/TypeVariable<+Ljava/lang/Class<+Ljava/util/ArrayList;>;>;
Signature: #76 // (TT;)V
public <T extends java.lang.Number> void setWithBound(T);
descriptor: (Ljava/lang/Number;)V
flags: (0x0001) ACC_PUBLIC
Code:
stack=0, locals=2, args_size=2
0: return
LineNumberTable:
line 29: 0
LocalVariableTable:
Start Length Slot Name Signature
0 1 0 this LGeneric;
0 1 1 t Ljava/lang/Number;
LocalVariableTypeTable:
Start Length Slot Name Signature
0 1 0 this LGeneric<TT;>;
0 1 1 t TT;
Signature: #80 // <T:Ljava/lang/Number;>(TT;)V
public T get();
descriptor: ()Ljava/lang/Object;
flags: (0x0001) ACC_PUBLIC
Code:
stack=1, locals=1, args_size=1
0: aconst_null
1: areturn
LineNumberTable:
line 32: 0
LocalVariableTable:
Start Length Slot Name Signature
0 2 0 this LGeneric;
LocalVariableTypeTable:
Start Length Slot Name Signature
0 2 0 this LGeneric<TT;>;
Signature: #83 // ()TT;
}
Signature: #84 // <T:Ljava/lang/Object;>Ljava/lang/Object;
SourceFile: "Generic.java"
在反编译后,Signature 属性存储者它的具体泛型信息,从上面可以看出
- 如果一个类中,它的字段,方法参数,返回值,或者类型参数,在编译期可以确定它的类型参 数,那么,在运行期也可以得到它的ParameterizedType,然后就可以获取它实际的泛型信息。
- 方法中的泛型信息无法被保留
具体的代码演示
import org.jetbrains.annotations.NotNull;
import java.lang.reflect.*;
import java.util.Arrays;
import java.util.stream.Collectors;
public class GenericTest {
public static void main(String[] args) throws Exception {
Generic<String> genericWithString = new Generic<>();
// 有一个类型变量
assert genericWithString.getClass().getTypeParameters().length == 1;
// 无法直接获取当前类的ParameterizedType,因为泛型
assert genericWithString.getClass().getTypeParameters()[0].getClass().isAssignableFrom(TypeVariable.class);
System.out.println("当前类的泛型信息:" + genericWithString.getClass().getTypeParameters()[0].getName());
Field map = genericWithString.getClass().getField("map");
Type type = map.getGenericType();
System.out.println("map的泛型信息:" + type);
Arrays.stream(genericWithString.getClass().getDeclaredMethods()).forEach(i -> {
System.out.printf("""
方法名: %s
方法的泛型信息: %s
方法的参数类型: %s
方法的返回类型: %s
%n""", i.getName(), i.getGenericReturnType(), getGenericParameterType(i), i.getGenericReturnType());
});
// 创建匿名对象时,匿名对象可以保存当前类传递给父类的类型参数
Generic generic1 = new Generic<Comparable<String>>() {
};
Type superclass = generic1.getClass().getGenericSuperclass();
System.out.println("generic1的泛型信息:" + superclass);
}
@NotNull
private static String getGenericParameterType(Method method) {
return Arrays.stream(method.getGenericParameterTypes()).map(i -> {
if (i instanceof TypeVariable<?>) {
return i + " : " + ((TypeVariable<?>) i).getBounds()[0].getTypeName();
} else {
return i.getTypeName();
}
}).collect(Collectors.joining(", "));
}
static void getGenericInfo(Object o) {
Class<?> aClass = o.getClass();
TypeVariable<?>[] typeParameters = aClass.getTypeParameters();
for (TypeVariable<?> typeVariable : typeParameters) {
System.out.println(typeVariable.getName());
}
}
}
输出结果为
当前类的泛型信息:T
map的泛型信息:java.util.Map<java.lang.String, Teacher>
方法名: get
方法的泛型信息: T
方法的参数类型:
方法的返回类型: T
方法名: set
方法的泛型信息: void
方法的参数类型: T : java.lang.Object
方法的返回类型: void
方法名: setList
方法的泛型信息: void
方法的参数类型: java.util.List<java.lang.Number>
方法的返回类型: void
方法名: getList
方法的泛型信息: java.util.List<java.lang.String>
方法的参数类型:
方法的返回类型: java.util.List<java.lang.String>
方法名: setWithBound
方法的泛型信息: void
方法的参数类型: T : java.lang.Number
方法的返回类型: void
generic1类的泛型信息:Generic<java.lang.Comparable<java.lang.String>>