您的位置 首页 java

Java类型系统

Java类型系统

Type

Type是所有类型的父接口, 如原始类型(raw types,对应Class)、 参数化类型(parameterized types, 对应ParameterizedType)、 数组类型(array types,对应GenericArrayType)、 类型变量(type variables, 对应TypeVariable)和基本(原生)类型(primitive types, 对应Class), 子接口有ParameterizedType, TypeVariable, GenericArrayType, WildcardType, 实现类有Class

ParameterizedType

  1. Type getRawType(): 返回承载该泛型信息的对象, 如上面那个Map承载范型信息的对象是Map
  2. Type[] getActualTypeArguments(): 返回实际泛型类型列表, 如Map<String,String>实际范型列表中有两个元素, 都是String
  3. Type getOwnerType(): 返回是谁的member.(上面那两个最常用)

TypeVariable

类型变量, 范型信息在编译时会被转换为一个特定的类型, 而TypeVariable就是用来反映在JVM编译该泛型前的信息. 它的声明是这样的: public interface TypeVariable extends Type 也就是说它跟GenericDeclaration有一定的联系, 我是这么理解的: TypeVariable是指在GenericDeclaration中声明的、这些东西中的那个变量T、C; 它有如下方法:

  1. Type[] getBounds(): 获取类型变量的上边界, 若未明确声明上边界则默认为Object
  2. D getGenericDeclaration(): 获取声明该类型变量实体
  3. String getName(): 获取在源码中定义时的名字

注意:

  • 类型变量在定义的时候只能使用extends进行(多)边界限定, 不能用super;
  • 为什么边界是一个数组? 因为类型变量可以通过&进行多个上边界限定,因此上边界有多个

GenericArrayType

范型数组,组成数组的元素中有范型则实现了该接口; 它的组成元素是ParameterizedType或TypeVariable类型,它只有一个方法:

  1. Type getGenericComponentType(): 返回数组的组成对象, 即被JVM编译后实际的对象

WildcardType

该接口表示通配符泛型, 比如? extends Number 和 ? super Integer 它有如下方法:

  1. Type[] getUpperBounds(): 获取范型变量的上界
  2. 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 属性存储者它的具体泛型信息,从上面可以看出

  1. 如果一个类中,它的字段,方法参数,返回值,或者类型参数,在编译期可以确定它的类型参 数,那么,在运行期也可以得到它的ParameterizedType,然后就可以获取它实际的泛型信息。
  2. 方法中的泛型信息无法被保留

具体的代码演示

 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>>  

文章来源:智云一二三科技

文章标题:Java类型系统

文章地址:https://www.zhihuclub.com/185185.shtml

关于作者: 智云科技

热门文章

网站地图