目录
1. 为什么使用泛型
2. 泛型的使用方式
2.1. 泛型类
2.2. 泛型接口
2.3. 泛型方法
3. 泛型涉及的符号
3.1. 类型通配符"?"
3.2. 占位符 T/K/V/E
3.3. 占位符T和通配符?的区别。
4. 泛型不变性
5. 泛型编译时擦除
1. 为什么使用泛型
Java 为什么使用泛型-CSDN博客
2. 泛型的使用方式
2.1. 泛型类
泛型类是用类型参数定义类的一种方式。这些类型参数在声明类变量或作为方法参数时会被具体的类型所替代。
public class Box<T> {
private T t;
public void set(T t) { this.t = t; }
public T get() { return t; }
}
2.2. 泛型接口
泛型接口和泛型类的定义方式类似。
public interface List<E> extends Collection<E> {
// ...
}
2.3. 泛型方法
泛型方法是在方法定义中声明类型参数的方法。
public static <T> T getFirst(List<T> list) {
if (list == null || list.isEmpty()) {
return null;
}
return list.get(0);
}
3. 泛型涉及的符号
3.1. 类型通配符"?"
如 ?、? extends T、? super T。用于表示未知的类型,或表示某个类型的子类型或超类型。
- 无界通配符“?“:
无界通配符表示未知的类型。当使用无界通配符时,编写时不能往这个通配符表示的集合中存放元素,但是可以从集合中获取元素(并且只能赋值给 Object
类型的变量或是进行类型转换)。这是因为编译器不知道集合中元素的具体类型,所以不能确保放入的元素与集合中已有的元素类型兼容。
但运行时可以赋值对象。
Class<?> clazz = Class.forName("com.mycompany.myreflect.Student");
- ? extends T:
表示未知的类型,但它是 T 或 T 的某个子类型。
我们就叫做上界限通配符,upper bounded wildcard。
当你需要读取集合中的元素,并且你知道元素的类型至少是 T
时,可以使用这种通配符。但是,你不能往这个集合中添加元素(除了 null
),因为编译器无法确保你要添加的元素与集合中已有的元素类型兼容。
- ? super T:
表示未知的类型,但它是 T 或 T 的某个超类型。
我们就叫做下界通配符, lower bounded wildcard。
当你需要向集合中添加元素,并且你知道这些元素的类型是 T
或其子类时,可以使用这种通配符。同时,你也可以从集合中读取元素,但是只能赋值给 Object
类型的变量或是 T
的超类型。
3.2. 占位符 T/K/V/E
T是占位符。其实它同K/V/E是一样的没有任何差别。只是我们的习惯会将它用在不同地方用于区别。
public class PrinterGen<T> {
//这个字符T,其实你可以使用你喜欢的字符代替,但是它必须和尖括号配合使用
//...
}
3.3. 占位符T和通配符?的区别。
- 用途:泛型主要用于定义可重用的类、接口和方法,其中类型参数在编译时确定。通配符主要用于表示对类型的约束或限制,通常用于泛型方法或泛型类的参数。
- 类型擦除:泛型在编译时会被类型擦除,而通配符在运行时仍然存在,用于表示对类型的约束。
- 编译时是否确定:如果编译时可以确定类型的,就可以使用T。而一定要等到运行时才能确定具体类型的就需要使用?
而申明方法,类型,接口时,只能使用T,不能使用?。也是由于我们申明的方法等,在编译时调用它的地方参数可以是不同的,但是必须是确定的。Class<?> clazz = Class.forName("com.mycompany.myreflect.Student"); System.out.println(clazz); Class<Student> clazz = Student.class; System.out.println(clazz);
4. 泛型不变性
Java中的泛型不变性(Generics Invariance)主要指的是泛型类型在编译时的类型安全性质,它确保了泛型类型在声明和使用时类型的一致性。
这是因为虽然String是Object的子类,但是List<String>并不是List<Object>的子类。
5. 泛型编译时擦除
Java的泛型类型信息是在编译时被擦除的,而不是在运行时。这是Java泛型实现的一个重要特性,称为类型擦除(Type Erasure)。
在编译时,Java编译器会处理泛型代码,生成不包含泛型类型信息的字节码。具体来说,编译器会将泛型类型参数替换为它们的上界(通常是Object
,除非明确指定了其他上界),并插入必要的类型转换和类型检查代码以确保类型安全。这个过程被称为类型擦除。
在运行时,Java虚拟机(JVM)加载并运行这些已经过类型擦除的字节码。由于泛型类型信息已经被擦除,JVM不知道也不关心这些类型参数。它只看到普通的类和接口,以及普通的方法调用和字段访问。
因此,虽然泛型提供了类型安全和更好的代码可读性,但它们并不会影响Java程序的运行时行为。泛型主要是一种编译时的语法糖,用于提高代码的可读性和类型安全性,而不会增加任何运行时开销。
源代码
编译后