Java reflect Type类及其子类用法分析

本文详细介绍了Java泛型系统的组成部分,包括Type接口及其子接口ParameterizedType、GenericArrayType、TypeVariable和WildcardType,并通过实例展示了如何使用这些类型。

在看源码之前我们先来了解一下什么是Type

首先:Type 是 Java 编程语言中所有类型的公共高级接口。它们包括原始类型(Class)、参数化类型(Parameterized)、数组类型(GenericArrayType)、类型变量(TypeVariable)和基本类型(Class)。

首先我们先来看看其定义:

public interface Type {
    default String getTypeName() {		//返回一个字符串描述该类型
        return toString();
    }
}
我们再来看看其继承体系


根据类图我们可以看到Type拥有4个已知的子类:

GenericArrayType 表示一种数组类型,其组件类型为参数化类型或类型变量。 

ParameterizedType 表示参数化类型,如 Collection<String>

TypeVariable 是各种类型变量的公共高级接口。

WildcardType 表示一个通配符类型表达式,如 ?? extends Number? super Integer。 

以上为JDK API中的解释,看了似乎并没有搞明白其想说明什么

-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

A.首先我们看一下ParameterizedType的接口定义

/**
 * ParameterizedType表示参数化类型,也就是泛型,例如List<T>、Set<T>等;
 */
public interface ParameterizedType extends Type {

    //获取泛型中的实际类型,可能会存在多个泛型,例如Map<K,V>,所以会返回Type[]数组;
    Type[] getActualTypeArguments();

    //获取声明泛型的类或者接口,也就是泛型中<>前面的那个值;
    Type getRawType();
    
    //通过getOwnerType()方法可以获取到内部类的“拥有者”;
    //拥有者表示的含义--内部类的“父类”,例如: Map  就是 Map.Entry<K,V>的拥有者。
    Type getOwnerType();
}
例子:

在下面的例子中ParamterizedType类拥有两个成员变量List<T> ,Set<String>我们获取到该类型之后会发现,他们都是ParamterizedTypeImpl类型,因为Set<String>和Set<Integer>实际上是一样的,都是泛型。

public class ParameterizedTypeTest<T> {
    private List<T> list=null;
    private Set<String> set=null;

    public static void main(String[] args) throws NoSuchFieldException{
        Field fieldList=ParameterizedTypeTest.class.getDeclaredField("list");
        //获取该属性的泛型类型
        Type typeList=fieldList.getGenericType();
        //获取实际的Type类型
        System.out.println(typeList.getClass().getName());  //输出为: sun.reflect.generics.reflectiveObjects.ParameterizedTypeImpl

        Field fieldSet=ParameterizedTypeTest.class.getDeclaredField("set");
        //获取该属性的泛型类型
        Type typeSet=fieldList.getGenericType();
        //获取实际的Type类型
        System.out.println(typeSet.getClass().getName());  //输出为: sun.reflect.generics.reflectiveObjects.ParameterizedTypeImpl
    }
}

我们再来看看其接口方法

首先是getActualTypeArguments  : 根据名称,其实也可以猜出其作用:就是获取泛型参数的实际类型。有一点需要注意的是,Java的泛型采用的是擦除发实现了,那么在运行时又能动态的获取泛型的实际类型,就足以证明,泛型类型还是通过某种方式写入到了class文件中

例子如下:

public class ParameterizedTypeTest<T> {
    private List<T> list=null;
    private Set<Integer> set=null;
    private Map<String,Integer> map=null;
    private Map<Map<String,Integer>,Map<Object,Object>> map2=null;

    /**
     * 如果是ParameterizedTypeTest.class.getDeclaredField("map2");
     * 那么结果如下:
     * System.out.println(types[0]);  //输出为:java.util.Map<java.lang.String, java.lang.Integer>
     * System.out.println(types[1]);  //输出为:java.util.Map<java.lang.Object, java.lang.Object>
     */
    public static void testGetActualTypeArguments()throws NoSuchFieldException{
        Field fieldMap=ParameterizedTypeTest.class.getDeclaredField("map");
        Type typeMap=fieldMap.getGenericType();
        ParameterizedType parameterizedTypeMap=(ParameterizedType)typeMap;
        Type[] types=parameterizedTypeMap.getActualTypeArguments();
        System.out.println(types[0]);   //输出为:class java.lang.String
        System.out.println(types[1]);  //输出为:class java.lang.Integer
    }

    public static void main(String[] args) throws NoSuchFieldException{
        testGetActualTypeArguments();
    }
}
再来看看getRawType方法:获取声明泛型的类或者接口,说明了也就是这个泛型是被谁声明的,例子如下:

public class ParameterizedTypeTest<T> {
    private List<T> list=null;
    private Set<Integer> set=null;
    private Map<String,Integer> map=null;

    @Test
    public void testGetRawType() throws NoSuchFieldException{
        Field fieldMap=ParameterizedTypeTest.class.getDeclaredField("map");
        //getGenericType() 返回一个 Type 对象,它表示此 Field 对象所表示字段的声明类型
        Type typeMap=fieldMap.getGenericType();
        ParameterizedType parameterizedTypeMap= (ParameterizedType) typeMap;
        Type type=parameterizedTypeMap.getRawType();
        System.out.println(type.getTypeName()); //输出  java.util.Map
    }
}

最后再来getOwnerType()方法

public class ParameterizedTypeTest<T> {

    private Map.Entry<String,Integer> mapEntry;

    @Test
    public void testGetRawType() throws NoSuchFieldException{
        Field fieldMap=ParameterizedTypeTest.class.getDeclaredField("mapEntry");
        //getGenericType() 返回一个 Type 对象,它表示此 Field 对象所表示字段的声明类型
        Type typeMap=fieldMap.getGenericType();
        ParameterizedType parameterizedTypeMap= (ParameterizedType) typeMap;
        Type type=parameterizedTypeMap.getRawType();
        System.out.println(type.getTypeName()); //输出  java.util.Map$Entry
        System.out.println(parameterizedTypeMap.getOwnerType());    //输出   interface java.util.Map

    }
}

-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

再来看看GenericArrayType

首先先看看定义

/**
 * 泛型数组类型,例如List<String>[] 、T[]等
 */
public interface GenericArrayType extends Type {
    /**
     * 返回泛型数组中元素的Type类型,即List<String>[] 中的 List<String>(ParameterizedTypeImpl)、
     * T[] 中的T(TypeVariableImpl)
     */
    Type getGenericComponentType();
}
例子:

public class GenericArrayTypeTest<T> {

    private T[] t;
    private List<String>[] listArray;

    @Test
    public void TestGetGenericComponentType()throws NoSuchFieldException{
        Field fieldListArray=GenericArrayTypeTest.class.getDeclaredField("listArray");
        Type typeListArray=fieldListArray.getGenericType();
        System.out.println(typeListArray.getClass().getName()); //sun.reflect.generics.reflectiveObjects.GenericArrayTypeImpl

        Field fieldT =GenericArrayTypeTest.class.getDeclaredField("t");
        Type typeT=fieldT.getGenericType();
        System.out.println(typeT.getClass().getName());         //sun.reflect.generics.reflectiveObjects.GenericArrayTypeImpl
    }   
}

通过上述例子可以看出:GenericArrayType表示的是泛型数组类型

再来看看其接口方法:getGenericComponentType

public class GenericArrayTypeTest<T> {

    private T[] t;
    private List<String>[] listArray;
    
    public void TestGetGenericComponentType(String filedName)throws NoSuchFieldException{
        Field fieldListArray=GenericArrayTypeTest.class.getDeclaredField(filedName);
        Type typeListArray=fieldListArray.getGenericType();
        GenericArrayType genericArrayType=(GenericArrayType)typeListArray;
        Type type=genericArrayType.getGenericComponentType();
        System.out.println(type.getTypeName());
    }

    @Test
    public void print()throws NoSuchFieldException{
        TestGetGenericComponentType("listArray");   //输出 java.util.List<java.lang.String>
        TestGetGenericComponentType("t");           //输出 T
    }
}

-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

再来看看TypeVariable

/**
 * 泛型的类型变量,指的是List<T>、Map<K,V>中的T,K,V等值,
 * 实际的Java类型是TypeVariableImpl(TypeVariable的子类);
 * 此外,还可以对类型变量加上extend限定,这样会有类型变量对应的上限;
 */
interface TypeVariable<D extends GenericDeclaration> extends Type, AnnotatedElement {

    /**
     * 获得该类型变量的上限,也就是泛型中extend右边的值;
     * 例如 List<T extends Number> ,Number就是类型变量T的上限;
     * 如果我们只是简单的声明了List<T>(无显式定义extends),那么默认为Object;
     * 值得注意的是,类型变量的上限可以为多个,必须使用&符号相连接,
     * 例如 List<T extends Number & Serializable>;其中,& 后必须为接口;
     */
    Type[] getBounds();

    //获取声明该类型变量实体
    D getGenericDeclaration();

    //获取类型变量在源码中定义的名称;
    String getName();

    //这个方法是JDK1.8中的新方法,目前还不清楚其作用
    AnnotatedType[] getAnnotatedBounds();
}
先来看看用法:

public class TypeVariableTest<T> {
    private List<T> list;
    @Test
    public void testTypeVariable()throws NoSuchFieldException{
        Field fieldList=TypeVariableTest.class.getDeclaredField("list");
        Type typeList=fieldList.getGenericType();
        ParameterizedType parameterizedTypeList=(ParameterizedType)typeList;
        Type[] types=parameterizedTypeList.getActualTypeArguments();
        System.out.println(types[0].getClass().getName());      //输出    sun.reflect.generics.reflectiveObjects.TypeVariableImpl
    }
}
再来看看getBounds()方法

public class TypeVariableTest<T extends Number & Serializable & Comparable> {
   private T t;
    @Test
    public void testGetBounds()throws NoSuchFieldException{
        Field fieldT=TypeVariableTest.class.getDeclaredField("t");
        TypeVariable typeVariable= (TypeVariable) fieldT.getGenericType();
        Type[] types=typeVariable.getBounds();
        for(Type type : types){
            System.out.print(type + " ");     //输出 class java.lang.Number interface java.io.Serializable interface java.lang.Comparable 
        }
    }
}

默认的边界测试

public class TypeVariableTest<T> {
    private List<T> list;
    @Test
    public void testTypeVariable()throws NoSuchFieldException{
        Field fieldList=TypeVariableTest.class.getDeclaredField("list");
        Type typeList=fieldList.getGenericType();
        ParameterizedType parameterizedTypeList=(ParameterizedType)typeList;
        Type[] types=parameterizedTypeList.getActualTypeArguments();
        TypeVariable typeVariable=(TypeVariable)types[0];
        Type[] types2=typeVariable.getBounds();
        for(Type type : types2){
            System.out.print(type + " ");     //输出 class java.lang.Object 
        }
    }
}

再来看看getGenericDeclaration方法

public class TypeVariableTest<T> {
    private T t;

    @Test   //获取声明该类型变量实体,也就是TypeVariableTest<T>中的TypeVariableTest;
    public void testGetGenericDeclaration()throws NoSuchFieldException{
        Field fieldT=TypeVariableTest.class.getDeclaredField("t");
        TypeVariable typeVariable=(TypeVariable)fieldT.getGenericType();
        GenericDeclaration genericDeclaration=typeVariable.getGenericDeclaration();
        System.out.println(genericDeclaration);     //输出 class TypeVariableTest
    }
}

里面涉及到了一个类 GenericDeclaration  顺便看看该类

含义为:声明类型变量的所有实体的公共接口;也就是说该接口定义了哪些地方可以定义类型变量(泛型);该类的继承结构如下:

也就是说,只有在以上三种子类中才可以定义泛型

public class TestGenericDeclarationForClass<T> {
        //测试在Class定义泛型
}

class TestGenericDeclarationForConstruct{
    public <T> TestGenericDeclarationForConstruct(T t){
        //测试在构造方法定义泛型
    }
}

class TestGenericDeclarationForMethod{

    public <T> void test(T t){
        //测试在方法定义泛型
    }
}
再来看看方法getName  这个方法就比较好理解了

public class TypeVariableTest<T> {
    private T t;

    @Test
    public void testGetName()throws NoSuchFieldException{
        Field fieldT=TypeVariableTest.class.getDeclaredField("t");
        TypeVariable typeVariable=(TypeVariable)fieldT.getGenericType();
        System.out.println(typeVariable.getName()); //输出 T
    }
}

-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

再来看看Class类 (Class 类就不是接口,而是实现类了)

Type接口的实现类,是我们工作中常用到的一个对象;在Java中,每个.class文件在程序运行期间,都对应着一个Class对象,这个对象保存有这个类的全部信息;因此,Class对象也称之为Java反射的基础;

public class ClassTest {
    private ClassTest classTest;

    @Test
    public void testClass()throws NoSuchFieldException{
        Field field=ClassTest.class.getDeclaredField("classTest");
        Type type=field.getGenericType();
        System.out.println(type);   //class ClassTest
    }
}

当我们没有声明泛型的时候,我们普通的对象就是一个Class类型,是Type中的一种;

----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

最后再来看看 WildcardType

定义如下:

/**
 * 通配符表达式,表示通配符泛型
 * 例如:List<? extends Number> 和 List<? super Integer>;
 */
public interface WildcardType extends Type {

    //获取泛型变量的上边界(extends)
    Type[] getUpperBounds();

    //获取泛型变量的下边界(super)
    Type[] getLowerBounds();
}
获取该对象例子:

public class WildcardTypeTest {
    private List<? extends Number> listNum;

    private List<? super String> listStr;

    public void testWildcardType(String fieldName)throws NoSuchFieldException{
        Field fieldNum=WildcardTypeTest.class.getDeclaredField(fieldName);
        ParameterizedType parameterizedTypeNum= (ParameterizedType) fieldNum.getGenericType();
        Type[] types=parameterizedTypeNum.getActualTypeArguments();
        System.out.println(types[0].getClass());
    }

    @Test
    public void print()throws NoSuchFieldException{
        testWildcardType("listNum");    //输出 class sun.reflect.generics.reflectiveObjects.WildcardTypeImpl
        testWildcardType("listStr");    //输出 class sun.reflect.generics.reflectiveObjects.WildcardTypeImpl
    }
}

在来看看方法getUpperBounds()

public class WildcardTypeTest {
    private List<? extends Number> listNum;

    private List<? super String> listStr;

    public void testWildcardType(String fieldName)throws NoSuchFieldException{
        Field fieldNum=WildcardTypeTest.class.getDeclaredField(fieldName);
        ParameterizedType parameterizedTypeNum= (ParameterizedType) fieldNum.getGenericType();
        Type[] types=parameterizedTypeNum.getActualTypeArguments();
        WildcardType wildcardType=(WildcardType)types[0];
        System.out.println(wildcardType.getUpperBounds()[0]);
        //System.out.println(wildcardType.getLowerBounds()[0]);
    }

    @Test
    public void print()throws NoSuchFieldException{
        testWildcardType("listNum");    //输出 class java.lang.Number
    }
}

最后再来看看方法getLowerBounds()

public class WildcardTypeTest {
    private List<? extends Number> listNum;

    private List<? super String> listStr;

    public void testWildcardType(String fieldName)throws NoSuchFieldException{
        Field fieldNum=WildcardTypeTest.class.getDeclaredField(fieldName);
        ParameterizedType parameterizedTypeNum= (ParameterizedType) fieldNum.getGenericType();
        Type[] types=parameterizedTypeNum.getActualTypeArguments();
        WildcardType wildcardType=(WildcardType)types[0];
        //System.out.println(wildcardType.getUpperBounds()[0]);
        System.out.println(wildcardType.getLowerBounds()[0]);
    }

    @Test
    public void print()throws NoSuchFieldException{
        testWildcardType("listStr");    //输出 class java.lang.String
    }
}









<think>嗯,用户现在想了解Java中的Type的具体定义和使用场景,特别是与泛型相关的部分。我需要先回顾一下之前的对话,确保信息一致。用户之前问过关于泛型擦除和如何获取泛型型的问题,我的回答里提到了Type接口和ParameterizedTypeGenericArrayType等子接口,以及使用反射的例子。现在用户可能想更深入地了解Type本身的结构和实际应用场景。 首先,我需要确认Type接口在Java中的位置。Typejava.lang.reflect包中的一个接口,自Java 1.5引入,用于支持泛型的反射。接下来,Type有四个子接口:Class、ParameterizedTypeTypeVariableGenericArrayType,还有一个WildcardType可能在某些资料中被提到,但根据之前的引用,用户提供的引用中没有提到,可能需要确认。不过根据常见的知识,WildcardType确实属于Type的子接口,用来表示通配符型,如<? extends Number>。 然后,具体定义部分需要详细说明每个子接口的作用。例如,Class代表原始型或基本型,ParameterizedType表示参数化型,如List<String>,TypeVariable型变量,如泛型中的T,GenericArrayType表示泛型数组,如T[]。WildcardType处理通配符型。 使用场景方面,用户提到的泛型相关,所以需要结合反射处理泛型的情况。例如,在框架中解析JSON到泛型对象时,需要获取List<User>的具体型,这时候就需要通过Type来获取实际的参数型。另外,在动态代理、依赖注入等高级功能中也会用到Type来获取型信息。 在之前的回答中,用户提供的引用[4]提到了处理泛型擦拭对象时使用GenericArrayTypeTypeVariable,引用[1]和[5]涉及泛型接口和方法的实现。需要将这些内容整合到Type的使用场景中,例如如何通过Type接口的子类来获取泛型的具体型参数,这在反序列化或者实例化泛型对象时非常关键。 用户可能存在的疑问是,为什么需要Type接口以及它的子接口,直接使用Class不行吗?这里需要解释由于型擦除,运行时无法通过Class对象获取泛型信息,而Type接口及其子接口提供了更详细的型元数据,允许在运行时通过反射访问这些信息。 另外,需要给出具体的代码示例,比如如何获取一个泛型的参数化型,如何使用TypeToken模式(如Gson中的实现)来保留型信息。例如,引用[3]中的泛型方法,结合Type的使用来实例化对象。 可能还需要注意Type接口在实际开发中的常见应用,比如在Spring框架中处理依赖注入时的型解析,或者在Jackson库中反序列化泛型集合时使用TypeReference来传递型信息。 最后,用户的问题可能还涉及到如何区分不同的Type子接口,以及它们在不同场景下的应用。需要明确每个子接口的适用情况,例如ParameterizedType用于带有泛型参数的型,而TypeVariable用于表示泛型型变量。 总结来说,回答的结构应该是先定义Type接口及其子类,然后分别介绍每个子类的定义和使用场景,结合泛型相关的示例代码,最后总结应用场景和注意事项。需要确保引用之前的引用内容,如引用[1]、[2]、[3]、[4]、[5]中的相关内容,尤其是引用[4]中提到的处理泛型擦拭对象的方法。</think>### Java中`Type`的定义与使用场景(泛型相关) --- #### **一、`Type`的定义** `Type`是Java反射API中**描述型信息的核心接口**,位于`java.lang.reflect`包中,自**Java 1.5**引入以支持泛型反射。它包含以下子接口/实现: | 子接口/ | 描述 | |----------------------|--------------------------------------------------------------------| | `Class` | 表示原始型(如`String`)或未参数化的泛型型(如`List`) | | `ParameterizedType` | 表示参数化型(如`List<String>`) | | `TypeVariable` | 表示泛型型变量(如`T`在`class Box<T>`中) | | `GenericArrayType` | 表示泛型数组(如`T[]`) | | `WildcardType` | 表示通配符型(如`<? extends Number>`) | --- #### **二、核心子接口详解** ##### **1. `ParameterizedType`(参数化型)** - **定义**:描述带有具体泛型参数的型(如`Map<String, Integer>`)。 - **核心方法**: ```java Type[] getActualTypeArguments(); // 获取泛型参数(如String和Integer) Type getRawType(); // 获取原始型(如Map) Type getOwnerType(); // 获取外部型(如Map.Entry的Owner是Map) ``` - **使用场景**: 解析泛型的具体参数,例如反序列化JSON到`List<User>`时需提取`User.class`[^4]。 ##### **2. `TypeVariable`(型变量)** - **定义**:表示泛型/方法中声明的型变量(如`T`)。 - **核心方法**: ```java String getName(); // 获取型变量名(如"T") Type[] getBounds(); // 获取型边界(如<T extends Number>) ``` - **使用场景**: 动态分析泛型型约束,例如生成泛型文档或校验型安全[^5]。 ##### **3. `GenericArrayType`(泛型数组)** - **定义**:表示由泛型型组成的数组(如`List<String>[]`)。 - **核心方法**: ```java Type getGenericComponentType(); // 获取数组元素型(如List<String>) ``` - **使用场景**: 处理复杂泛型结构,例如框架中解析`T[]`的JSON数组[^4]。 ##### **4. `WildcardType`(通配符型)** - **定义**:描述通配符表达式(如`<? extends Number>`)。 - **核心方法**: ```java Type[] getUpperBounds(); // 获取上界(如Number) Type[] getLowerBounds(); // 获取下界(如super Integer) ``` - **使用场景**: 处理泛型集合的协变/逆变(如`List<? extends Serializable>`)[^4]。 --- #### **三、`Type`的使用场景** ##### **1. 泛型反射与型擦除补偿** - **问题**:Java泛型在编译后会被擦除,运行时无法直接通过`Class`获取泛型参数。 - **解决方案**: 通过`Type`子接口(如`ParameterizedType`)提取泛型信息。 ```java public class StringList extends ArrayList<String> {} // 子类保留泛型信息 Type type = StringList.class.getGenericSuperclass(); // 获取父型 if (type instanceof ParameterizedType) { ParameterizedType pt = (ParameterizedType) type; Type[] args = pt.getActualTypeArguments(); // 输出:String.class } ``` ##### **2. JSON反序列化(如Gson/Jackson)** - **场景**:将JSON字符串`[{"name":"Alice"}]`转换为`List<User>`。 - **实现**: 使用`TypeToken`(Gson)或`TypeReference`(Jackson)保留泛型信息。 ```java // Gson示例 Type userListType = new TypeToken<List<User>>(){}.getType(); List<User> users = gson.fromJson(jsonStr, userListType); ``` 其原理是通过匿名内部继承`TypeToken`,利用反射获取`ParameterizedType`。 ##### **3. 动态代理与依赖注入** - **场景**:Spring框架中根据泛型型注入Bean。 - **示例**: ```java public class UserRepository<T extends Entity> { private Class<T> entityType; public UserRepository() { // 通过Type获取T的具体Type type = getClass().getGenericSuperclass(); ParameterizedType pt = (ParameterizedType) type; entityType = (Class<T>) pt.getActualTypeArguments()[0]; } } ``` ##### **4. 泛型方法返回值解析** - **场景**:通过反射获取泛型方法的返回型。 ```java public class Demo { public <T> List<T> createList() { return new ArrayList<>(); } public static void main(String[] args) throws NoSuchMethodException { Method method = Demo.class.getMethod("createList"); Type returnType = method.getGenericReturnType(); // 返回List<T> if (returnType instanceof ParameterizedType) { Type[] args = ((ParameterizedType) returnType).getActualTypeArguments(); System.out.println(args[0]); // 输出:T(TypeVariable) } } } ``` --- #### **四、关键注意事项** 1. **型擦除的局限性** - 泛型未指定具体型时(如`List`而非`List<String>`),无法通过`Type`获取参数信息[^4]。 - 匿名内部子类是保留泛型信息的常见手段(如`TypeToken`)[^4]。 2. **反射性能与安全** - 频繁使用反射可能影响性能,需结合缓存优化。 - 强制型转换需确保型安全,避免`ClassCastException`。 --- ### 相关问题 1. **如何通过`Type`接口实现自定义泛型序列化工具?** 2. **`WildcardType`在哪些实际场景中会被使用?** 3. **Spring框架如何利用`Type`接口处理泛型依赖注入?** 4. **如何区分`ParameterizedType`和`GenericArrayType`的适用场景?** --- [^1]: Java泛型接口与的定义 [^2]: 泛型方法的声明规则 [^4]: 反射处理泛型擦除的底层原理 [^5]: 泛型在框架中的高级应用
评论 1
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值