在运行时获取泛型的类型

转自:http://www.dongliu.net/post/5896921377931264


都Java 1.5在加入泛型支持时,为了保持兼容,采用的是擦除法实现,泛型的定义只在编译的时候有效,编译之后是没有保留泛型的类型信息的,运行时所有的instance都当作限界类型来使用,没有指定限界类型是则是当作Object类型。所以,通常是无法在运行时获得泛型的类型活实例化泛型类的,如下面的代码都无法通过编译:

public class Test<T> {
    public Test(){
        T t = new T();    // error
        Class<T> clazz = T.class;   //error
        T[] ts = new T[10];   //error
    }
}
然而,擦除法的实现存在一些特列,在这些特例情况下,Java会记录泛型的类型信息,并且可以通过Api来获取的。比如在各种持久化框架中广泛使用的BaseDao泛型类的写法,就利用一个泛型继承的特例:

public class BaseDAO<U> {
    private Class<U> entityClass;

    protected BaseDAO() {
        // 获取泛型类型
        Type type = getClass().getGenericSuperclass();  
        Type[] trueType = ((ParameterizedType) type).getActualTypeArguments();
        this.entityClass = (Class<U>) trueType[0];
    }
       
    public U getById(Serializable id){
        PersistenceManager pm = getPersistenceManager();
        pm.setDetachAllOnCommit(true);
        U object;
        try{
            object = pm.getObjectById(entityClass, id);
        }catch (JDOObjectNotFoundException e) {
            object = null;
        }finally{
            pm.close();
        }
        return object;
    }
}

public class TagDAO extends BaseDAO<Tag>{
}
代码的关键是getGenericSuperclass方法,这个方法返回的类型为ParameterizedType,里面可以通过getActualTypeArguments来获取泛型的真正类型。这样的特例是违背了擦除法的实现思想的,这也是Java为保证类型系统完整而不得不做的妥协。

Jackson json中反序列化时,可以通过TypeReference传递容器中的泛型类型,也是利用了泛型继承的特例:
Map<Integer, ApkBean> map = mapper.readValue(jsonData, new TypeReference<Map<Integer, ApkBean>>(){});
TypeReference是一个泛型抽象类,在readValue的第二个方法中,传入了TypeReference的一个匿名子类实例,由此带入了Map的泛型信息。由于是两级泛型的嵌套,具体的情况其实更复杂一些,有兴趣的可以看看Jackson代码中TypeReference和TypeFactory的实现。

除了泛型继承这种情况之外,还有另外两个特例,也可以获取ParameterizedType类型,继而获取到泛型的类型。一个是类的field可以通过getGenericType来获取:
public class Test {

    private Map<String, Number> map;

    public static void main(String[] args) throws NoSuchFieldException {
        Class<?> clazz = Test.class;
        Field field = clazz.getDeclaredField("map");
        //取得泛型类型
        Type type = field.getGenericType();
        ParameterizedType ptype = (ParameterizedType)type;
        System.out.println(ptype.getActualTypeArguments()[0]);
        System.out.println(ptype.getActualTypeArguments()[1]);
    }

}
另外就是方法的泛型参数可以通过getGenericParameterTypes来获取:
public class Test {

    public static void main(String[] args) throws NoSuchMethodException {
        Class<?> clazz = Test.class;
        Method method = clazz.getDeclaredMethod("getGenericSample", Collection.class);
        //取得泛型类型参数集
        Type[] type = method.getGenericParameterTypes();
        ParameterizedType ptype = (ParameterizedType)type[0];
        type = ptype.getActualTypeArguments();
        System.out.println(type[0]);
    }

    public void getGenericSample(Collection<Number> collection){

    }
}

这两种特例的实用价值比较小,实际的代码hack中也比较少见。


<think>首先,用户的问题是:"我想了解如何在Unity的方法中获取T的具体类型"。用户提到了Unity的方法,所以这可能与C#编程在Unity环境中相关。从系统指令中,我需要:-使用$...$forin-linemath。-独立公式用$$...$$。-LaTeX语法正确。-使用中文回答。-生成相关问题。-引用段落末尾加引用标识,如[^1]。参考提供的引用:-引用[1]讨论了约束,如whereT:class?,whereT:notnull等。-引用[2]展示了如何在Unity中使用,包括定义类和约束。用户真正的需求是获取T的具体类型方法中。在C#中,要获取类型参数T的实际类型,可以使用typeof(T)。在C#中:-当你在方法中编写代码,typeof(T)会返回Type对象表示T的具体类型。-这在运行可用,因为在C#中支持反射。例如:publicvoidPrintType<T>(){Typetype=typeof(T);Debug.Log($"ThetypeofTis:{type.Name}");}在Unity中,你可以通过这个方法来获取实际的T类型。用户提到Unity,所以我应该确保答案符合Unity的C#用法。Unity使用C#,所以标准C#反射机制应该工作。现在,构建回答:-结构清晰:先解释方法,然后给出示例。-使用中文。-包括代码示例,但根据系统指令,代码块需要适当格式化。系统指令中提到在回答中使用代码块,如示例中的Python代码。系统指令中的示例:```pythondefquick_sort(arr):...```所以,我可以使用类似的三重引号包裹代码块。最终,添加相关问题。回答大纲:1.**介绍问题**:解释在Unity的方法中如何获取T的具体类型。2.**解决方案**:说明使用typeof(T)。3.**代码示例**:给一个Unity脚本中的例子。4.**注意事项**:可能提到运行行为。5.**相关问题**:生成几个相关问题。生成相关问题:-例如:-如何在Unity中运行检查类型的属性?-如何反射类型的方法?-有什么局限性或性能考虑?确保引用格式:如果在回答中引用站内引用,末尾加[^1]。但用户提供了引用[1]和[2],所以我应该在相关部分提到它们。用户的引用是关于约束的,但我的回答焦点是获取具体类型。所以,如果相关,我可以引用这些概念。例如,在讨论约束,提到引用[1]的约束信息。核心方法不需要直接约束,但typeof(T)在所有情况下工作。现在,编写回答。首先:回答问题。</think> 在Unity中使用,你经常需要在方法内获取参数T的具体类型。在C#中(Unity基于C#),可以通过 `typeof(T)` 操作符在运行动态获取T的实际类型,这利用了C#的反射机制。以下是详细解释和逐步指南,帮助你理解和实现这一需求。回答参考了站内引用中的约束概念(如 `where T : class?` 确保类型有效性),但重点聚焦于获取具体类型本身[^1][^2]。 ### 步骤说明 1. **理解反射机制**: - 在C#中,在编译是抽象的,但 `typeof(T)` 在运行返回一个 `System.Type` 对象,包含T的实际类型信息。 - 这对于Unity脚本非常有用,例如在日志输出、类型检查或动态调用。 - 参考约束(如引用[1]中提到的 `where T : notnull`),这些约束可以帮助确保T的有效性,但 `typeof(T)` 工作在所有情况下[^1]。 2. **核心方法:使用 `typeof(T)`**: - 在方法中,直接调用 `typeof(T)` 获取T的具体类型。 - 示例代码:一个简单的方法,打印T的实际类型名。 ```csharp using UnityEngine; using System; public class GenericTypeExample : MonoBehaviour { void Start() { // 调用方法,传入具体类型 PrintType<string>(); PrintType<int>(); } // 方法:获取并输出T的类型 public void PrintType<T>() { // 获取T的具体类型 Type type = typeof(T); // 输出类型名(例如:"System.String"或"System.Int32") Debug.Log($"参数T的具体类型是:{type.FullName}"); } } ``` - **解释**: - 在 `PrintType<T>` 中,`typeof(T)` 返回一个 `Type` 对象。 - `type.FullName` 提供了类型的全名(例如,传入 `string` 输出 `"System.String"`)。 - 在Unity编辑器中,运行此脚本,Console窗口会显示日志输出,如 `参数T的具体类型是:System.Int32`。 3. **高级用法:类型检查和操作**: - 使用获取的 `Type` 对象,可以进行更多操作: - **类型比较**:检查T是否是特定类型。 ```csharp if (typeof(T) == typeof(string)) { Debug.Log("T是字符串类型!"); } ``` - **反射调用**:动态调用类型的方法或访问属性(但需注意性能影响,Unity中反射可能降低效率)。 - **结合约束**:确保T的有效性后再获取类型。例如,参考引用[2]的示例,使用 `where T : class` 约束T为引用类型,以避免null问题[^2]。 ```csharp public void SafePrintType<T>() where T : class? { Type type = typeof(T); Debug.Log($"安全获取类型:{type.Name}"); } ``` - **注意事项**: - **运行行为**: `typeof(T)` 只在运行有效(编译T是未知的)。 - **性能考虑**:反射在Unity中可能增加开销,频繁调用应优化或缓存 `Type` 对象。 - **约束的作用**:如引用[1]所示,约束如 `where T : notnull` 可以在获取类型前确保T有效(e.g., 避免null类型错误)[^1]。 ### 示例场景 在Unity中,假设你有一个方法处理不同数据类型: - 参考引用[2]中的Water类,如果你需要在方法中根据T的动态类型调整行为: ```csharp public class WaterManager : MonoBehaviour { void Start() { CheckWaterType<string, WaterBase>(); } public void CheckWaterType<T, U>() where U : WaterBase { // 获取T的具体类型 Type tType = typeof(T); Debug.Log($"日期数据类型: {tType.Name}"); // 输出如 "String" 或 "Int32" // 同样获取U的具体类型(根据约束,U必须是WaterBase的子类) Type uType = typeof(U); Debug.Log($"水信息类型: {uType.Name}"); } } public class WaterBase { /* 定义属性 */ } ``` - 运行后,Console会显示具体类型名,帮助你在游戏逻辑中动态决策(e.g., 数据序列化或UI显示)。 ### 常见问题解答 - **Q: 如果T是嵌套,还能获取类型吗?** A: 是, `typeof(T)` 返回最外层类型,但你可以进一步使用 `GetGenericArguments()` 处理嵌套。 - **Q: 在Unity Editor脚本中是否可用?** A: 是, Unity的所有C#脚本都支持这个机制,包括Editor脚本。 - **Q: 有替代方案吗?** A: 另一种方法是传递类型参数(e.g., `void Method(Type t)`),但 `typeof(T)` 更简洁直接。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值