JAVA 泛型 - Class<T>

本文介绍了Java中Class&lt;T&gt;的泛型化及其在反射中的应用,展示了如何利用泛型提高反射代码的类型安全性,并给出了具体的示例。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Class<T>

类Class已经泛型化了,但是很多人一开始都感觉其泛型化的方式很混乱。Class<T>中类型参数T的含义是什么?事实证明它是所引用的类接口。怎么会是这样的呢?那是一个循环推理?如果不是的话,为什么这样定义它?

在以前的 JDK 中,Class.newInstance()方法的定义返回Object,您很可能要将该返回类型强制转换为另一种类型:

class Class {
Object newInstance();
}

但是使用泛型,您定义Class.newInstance()方法具有一个更加特定的返回类型:

class Class<T> {
T newInstance();
}

如何创建一个Class<T>类型的实例?就像使用非泛型代码一样,有两种方式:调用方法Class.forName()或者使用类常量X.class。Class.forName()被定义为返回Class<?>。另一方面,类常量X.class被定义为具有类型Class<X>,所以String.class是Class<String>类型的。

让Foo.class是Class<Foo>类型的有什么好处?大的好处是,通过类型推理的魔力,可以提高使用反射的代码的类型安全。另外,还不需要将Foo.class.newInstance()强制类型转换为Foo。

考虑一个方法,它从数据库检索一组对象,并返回 JavaBeans 对象的一个集合。您通过反射来实例化和初始化创建的对象,但是这并不意味着类型安全必须完全被抛至脑后。考虑下面这个方法:

public static<T> List<T> getRecords(Class<T> c, Selector s) {
// Use Selector to select rows
List<T> list = new ArrayList<T>();
for (/* iterate over results */) {
T row = c.newInstance(); // use reflection to set fields from result
list.add(row);
}
return list;
}

可以像下面这样简单地调用该方法:

List<FooRecord> l = getRecords(FooRecord.class, fooSelector);

编译器将会根据FooRecord.class是Class<FooRecord>类型的这一事实,推断getRecords()的返回类型。您使用类常量来构造新的实例并提供编译器在类型检查中要用到的类型信息。

<think>我们被要求解释Java类型参数`<T>`和通配符`<?>`的区别。根据提供的引用材料,我们可以总结如下: 引用[1]提到允许在编写类、接口和方法时使用参数化类型,使得代码更加灵活、通用和类型安全。 引用[2]和[3]详细介绍了通配符`<?>`, `<? extends T>`, `<? super T>`,并指出了它们的使用场景和区别。 引用[4]也讨论了这三种通配符的使用场景。 现在,我们重点区分`<T>`和`<?>`: 1. **定义位置不同**: - `<T>`:是类型参数(Type Parameter),用于定义类、接口或方法时声明类型变量。例如: ```java public class Box<T> { ... } // 类 public <T> void method(T t) { ... } // 方法 ``` - `<?>`:是通配符(Wildcard),用于使用时表示未知类型。它不能用于定义类或方法,只能用于变量声明、方法参数等。例如: ```java List<?> list = ...; // 可以接受任何类型的List ``` 2. **使用目的不同**: - `<T>`:用于在编写代码时声明一个类型变量,这个类型变量可以在整个类或方法中使用,代表一个具体的类型(尽管在定义时未知,但在使用时会被指定)。 - `<?>`:用于表示一个未知的类型,通常用于提高API的灵活性,允许接受多种类型。 3. **类型约束**: - `<T>`:在定义类或方法时,我们可以对`T`进行约束(如`<T extends Number>`),这样`T`就被限定为Number或其子类。 - `<?>`:通配符本身也可以有上界或下界,如`<? extends Number>`或`<? super Integer>`,但无界通配符`<?>`表示未知类型,没有任何约束(除了Object)。 4. **类型引用**: - 使用`<T>`时,我们可以用`T`作为类型来声明变量、参数、返回类型等。例如: ```java public T get() { ... } // 在类中 public <T> T method(T t) { return t; } // 在方法中 ``` - 使用`<?>`时,我们不能直接引用该类型(因为不知道具体类型),只能使用Object类型的方法(因为任何类都是Object的子类)。例如: ```java List<?> list = ...; Object o = list.get(0); // 正确,因为任何对象都是Object // 但你不能添加元素(除了null),因为类型未知 list.add(null); // 允许,因为null是所有引用类型的值 list.add("hello"); // 错误,因为不知道list的具体类型 ``` 5. **应用场景**: - `<T>`:当你需要在整个类或方法中多次引用同一个类型,或者需要执行类型相关的操作(如创建数组、类型转换等)时使用。 - `<?>`:当你需要编写一个可以操作多种类型容器的方法,且不关心容器中元素的具体类型时使用。例如,一个打印List中所有元素的方法: ```java public void printList(List<?> list) { for (Object o : list) { System.out.println(o); } } ``` 6. **类型安全**: - 使用`<T>`可以保证类型安全,因为类型在编译时就被确定,并且在整个类或方法中保持一致。 - 使用`<?>`同样保证类型安全,因为它限制了我们对容器的操作(例如,不能添加元素,只能读取为Object),从而避免类型错误。 **总结**: | 特性 | 类型参数 `<T>` | 通配符 `<?>` | |--------------|----------------------------------------|----------------------------------------| | 定义位置 | 类/接口/方法的定义 | 变量声明、方法参数等使用的地方 | | 是否可命名 | 是,`T`是一个类型变量名 | 否,`?`表示未知类型 | | 类型约束 | 可通过`extends`/`super`约束 | 可通过`extends`/`super`约束(如`? extends Number`) | | 类型引用 | 可以用`T`作为类型使用 | 只能作为未知类型,读取为Object | | 添加元素 | 可以添加`T`类型的对象 | 只能添加`null`(因为类型未知) | | 适用场景 | 需要多次引用同一类型 | 只需要读取,不需要知道具体类型 | **示例代码**: ```java // 使用类型参数T的方法 public static <T> void fillList(List<T> list, T item) { for (int i = 0; i < list.size(); i++) { list.set(i, item); } } // 使用通配符?的方法(只能读取) public static void printList(List<?> list) { for (Object o : list) { System.out.println(o); } } ``` 在`fillList`方法中,我们使用`<T>`,因为我们需要在方法中多次使用同一个类型`T`(作为参数类型和列表元素类型)。而在`printList`方法中,我们只需要读取元素,所以使用`<?>`即可。 [^1]: 变量,通配符 <?> ,原理及应用详解。 [^2]: 深度解析 Java 通配符 `<? super T>` 和 `<? extends T>`。 [^3]: Java中的通配符<?>和<? extends T>、<? super T>的区别。 [^4]: 中的通配符<?>、<? extends T>、<? super T>的使用场景。 §§相关问题§§ 1. Java中`<? extends T>`和`<? super T>`分别适用于什么场景? 2. 擦除对Java有什么影响? 3. 在定义类时,如何正确使用类型边界(extends/super)?</think>在 Java 中,类型参数 `<T>` 和通配符 `<?>` 是两种不同的概念,主要区别体现在**定义位置**、**使用场景**和**类型约束**上: --- ### 1. **定义位置与作用** | **类型** | **定义位置** | **作用** | |-------------------|-------------------------------|--------------------------------------------------------------------------| | **`<T>`** | 类/方法**定义**时声明 | 表示一个**具体的类型参数**,在代码中可被引用(如 `T` 可作为变量类型)[^1] | | **`<?>`** | 类/方法**使用**时声明 | 表示**未知类型**(通配符),仅用于接受任意类型,无法直接引用[^3] | **示例**: ```java // 定义时使用 <T>(类型参数) class Box<T> { private T value; // T 可被引用 } // 使用时使用 <?>(通配符) void printList(List<?> list) { // ? 不可被直接引用,只能通过 Object 操作 for (Object obj : list) System.out.println(obj); } ``` --- ### 2. **类型约束能力** | **类型** | **类型约束** | |-------------------|----------------------------------------------------------------------------| | **`<T>`** | 可添加边界:`<T extends Number>`(限定 `T` 必须是 `Number` 的子类)[^1] | | **`<?>`** | 无界通配符,表示完全未知的类型(等同于 `<? extends Object>`)[^3] | **示例**: ```java // <T> 可约束类型 <T extends Number> T sum(List<T> list) { ... } // <?> 无约束,但无法添加元素 void process(List<?> list) { list.add(new Object()); // 编译错误!无法确定具体类型 } ``` --- ### 3. **使用场景** | **场景** | **适用类型** | **原因** | |------------------------|--------------|--------------------------------------------------------------------------| | **定义类/方法** | `<T>` | 需在代码中引用具体类型(如字段、返回值)[^1] | | **读取集合内容** | `<?>` | 只需读取元素(协变),不关心具体类型[^3] | | **写入集合** | `<T>` 或 `<? super T>` | 需明确写入类型(`<?>` 无法写入)[^4] | | **实现类型安全API** | `<T>` | 需在编译期检查类型一致性(如 `Collections.sort()`)[^2] | **示例**: ```java // 场景1:定义方法(使用 <T>) public static <T> void addToList(List<T> list, T item) { list.add(item); // 安全,类型一致 } // 场景2:读取任意集合(使用 <?>) public static void printAll(List<?> list) { for (Object obj : list) System.out.println(obj); // 安全读取 } // 场景3:写入集合(使用 <? super T>) public static <T> void addNumbers(List<? super T> dest, T item) { dest.add(item); // 安全,dest 是 T 的父类型 } ``` --- ### 4. **核心区别总结** | **特性** | **`<T>`** | **`<?>`** | |----------------------|--------------------------------------|----------------------------------------| | **可引用性** | ✅ 可作为类型使用(如 `T value`) | ❌ 只能通过 `Object` 操作 | | **写入能力** | ✅ 可添加 `T` 类型元素 | ❌ 只能添加 `null` | | **读取能力** | ✅ 返回 `T` 类型 | ✅ 返回 `Object` 类型 | | **类型边界控制** | ✅ 支持 `extends`/`super` | ✅ 支持 `extends`/`super`(如 `<? extends Number>`) | | **适用阶段** | 定义阶段 | 使用阶段 | > 💡 **简单记忆**: > - 需要**操作具体类型**时用 `<T>`(如定义类、方法或写入数据)。 > - 只需**读取数据且不关心类型**时用 `<?>`。 [^1]: 变量,通配符 <?> ,原理及应用详解。 [^2]: 深度解析 Java 通配符 `<? super T>` 和 `<? extends T>`。 [^3]: Java中的通配符<?>和<? extends T>、<? super T>的区别。 [^4]: 中的通配符<?>、<? extends T>、<? super T>的使用场景。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值