泛型_19
泛型
泛型(Generics) 是 Java 5 引入的一项重要特性,它允许在定义类、接口和方法时使用类型参数,从而提高代码的复用性、类型安全性和可读性。泛型的核心思想是 参数化类型,即在使用时才指定具体的类型。
泛型的作用
- 类型安全:在编译时检查类型,避免运行时的类型转换错误
- 代码复用:通过泛型可以编写通用的代码,适用于多种类型。
- 可读性:泛型使代码更加清晰,减少了强制类型转换的需要
泛型的使用范围
- 泛型类:在类定义时使用类型参数。
- 泛型接口:在接口定义时使用类型参数。
- 泛型方法:在方法定义时使用类型参数
泛型类
-
泛型类是在类定义时使用类型参数的类。类型参数用尖括号 < > 表示,通常用单个大写字母(如 T、E、K、V 等)表示
// 定义一个泛型类 class Box<T> { private T value; public void setValue(T value) { this.value = value; } public T getValue() { return value; } }
public class GenericClassExample { public static void main(String[] args) { // 使用泛型类,指定类型为 Integer Box<Integer> integerBox = new Box<>(); integerBox.setValue(123); System.out.println("Integer Value: " + integerBox.getValue()); // 使用泛型类,指定类型为 String Box<String> stringBox = new Box<>(); stringBox.setValue("Hello"); System.out.println("String Value: " + stringBox.getValue()); } }
泛型接口
-
泛型接口是在接口定义时使用类型参数的接口
// 定义一个泛型接口 interface Pair<K, V> { K getKey(); V getValue(); } // 实现泛型接口 class OrderedPair<K, V> implements Pair<K, V> { private K key; private V value; public OrderedPair(K key, V value) { this.key = key; this.value = value; } @Override public K getKey() { return key; } @Override public V getValue() { return value; } }
泛型方法
-
泛型方法是在方法定义时使用类型参数的方法。类型参数用尖括号 < > 表示,放在方法的返回类型之前
class Utils { // 定义一个泛型方法 public static <T> void printArray(T[] array) { for (T element : array) { System.out.println(element); } } } public class GenericMethodExample { public static void main(String[] args) { // 使用泛型方法 Integer[] intArray = {1, 2, 3}; String[] strArray = {"A", "B", "C"}; Utils.printArray(intArray); // 输出: 1 2 3 Utils.printArray(strArray); // 输出: A B C } }
泛型的类型擦除
-
Java 的泛型是通过 类型擦除(Type Erasure) 实现的。在编译时,泛型类型参数会被擦除,替换为它们的上限(通常是 Object)
-
类型擦除是 Java 实现泛型的一种方式。泛型的引入主要是为了在编译阶段提供更严格的类型检查,同时允许代码的复用。然而,Java 为了兼容之前没有泛型的代码,采用了类型擦除机制。简单来说,类型擦除就是在编译过程中,将泛型类型信息从代码中移除,把泛型类型参数替换为具体的类型,这个具体类型通常是它们的上限(如果没有指定上限,默认上限是 Object),从而生成普通的、非泛型的字节码
-
当定义泛型类、接口或方法时,如果泛型类型参数没有指定边界,在编译时这些类型参数会被擦除为 Object 类型
-
如果泛型类型参数指定了边界,在编译时会被擦除为其边界类型
// 编译前的泛型类 class Box<T> { private T value; public void setValue(T value) { this.value = value; } public T getValue() { return value; } } // 编译后的类(类型擦除后) class Box { private Object value; public void setValue(Object value) { this.value = value; } public Object getValue() { return value; } }
泛型的通配符
-
无界通配符(<?>):表示任意类型
public void printList(List<?> list) { for (Object element : list) { System.out.println(element); } }
-
上界通配符(<? extends T>):表示类型是 T 或其子类
public void printNumbers(List<? extends Number> list) { for (Number number : list) { System.out.println(number); } }
-
下界通配符(<? super T>):表示类型是 T 或其父类
public void addNumbers(List<? super Integer> list) { list.add(123); }
extends 可以用于泛型类型参数的声明,用于约束类型参数的上限。
使用 extends 声明泛型类型参数上限
class GenericExample { // 使用 extends 声明泛型类型参数 T 必须是 Number 或其子类 <T extends Number> void genericMethod(T param) { System.out.println("Generic method with number parameter: " + param); } public static void main(String[] args) { GenericExample example = new GenericExample(); // 可以传入 Integer 类型,因为 Integer 是 Number 的子类 example.genericMethod(10); // 可以传入 Double 类型,因为 Double 是 Number 的子类 example.genericMethod(3.14); } } //在上述代码中, <T extends Number> 表示泛型类型参数 T 必须是 Number 或者 Number 的子类,这样就可以在方法中安全地处理 Number 类型及其子类的对象
super 不能直接用于泛型类型参数的声明,它主要用于泛型通配符的场景
super 表示类型参数的下界,编译器无法确定类型参数的具体范围(如 Number 或其父类),因此不能用于泛型类型参数
import java.util.ArrayList; import java.util.List; class GenericExample { // 使用 super 作为通配符,list 的元素类型必须是 Number 或其父类 static void addNumbers(List<? super Number> list) { list.add(10); list.add(3.14); } public static void main(String[] args) { List<Object> objectList = new ArrayList<>(); // 可以传入 List<Object>,因为 Object 是 Number 的父类 addNumbers(objectList); System.out.println(objectList); } } //在上述代码中,List<? super Number> 表示这个 List 的元素类型必须是 Number 或者 Number 的父类,这样在 addNumbers 方法中就可以安全地向 List 中添加 Number 类型的对象
泛型的继承关系
泛型类或接口可以像普通类或接口一样被继承或实现。子类或实现类可以选择保留泛型类型参数,也可以指定具体的类型
-
保留泛型类型参数
// 泛型父类 class Box<T> { private T value; public void setValue(T value) { this.value = value; } public T getValue() { return value; } } // 子类保留泛型类型参数 class StringBox<T> extends Box<T> { // 可以添加子类特有的方法 } //实现 public class GenericInheritanceExample { public static void main(String[] args) { StringBox<String> box = new StringBox<>(); box.setValue("Hello"); System.out.println(box.getValue()); // 输出: Hello } }
-
指定具体类型
// 子类指定具体类型 class IntegerBox extends Box<Integer> { // 可以添加子类特有的方法 } public class GenericInheritanceExample { public static void main(String[] args) { IntegerBox box = new IntegerBox(); box.setValue(123); System.out.println(box.getValue()); // 输出: 123 } }
泛型方法也可以被继承。子类可以重写父类的泛型方法,但必须保持方法签名一致
虽然泛型的名称(如
class Parent {
// 泛型方法
public <T> void print(T value) {
System.out.println("Parent: " + value);
}
}
class Child extends Parent {
// 重写泛型方法 其中代表泛型的字母可以不同
@Override
public <T> void print(T value) {
System.out.println("Child: " + value);
}
}
public class GenericMethodInheritanceExample {
public static void main(String[] args) {
Parent parent = new Parent();
parent.print("Hello"); // 输出: Parent: Hello
Child child = new Child();
child.print("Hello"); // 输出: Child: Hello
}
}
----------------------------------------------
class Parent {
<T extends Number> void genericMethod(T param) {
System.out.println("Parent's generic method");
}
}
class Child extends Parent {
// 泛型类型参数的边界保持一致
@Override
<T extends Number> void genericMethod(T param) {
System.out.println("Child's generic method");
}
}
泛型注意事项
-
泛型类型参数在运行时是不可见的
-
不能直接创建泛型类型的实例(如 new T())
-
不能直接创建泛型数组(如 new T[10])
-
不能使用基本类型:泛型类型参数必须是引用类型(如 Integer 而不是 int)
-
在指定泛型具体类型后,可以传入该类型或者其子类型
-
泛型类型的继承关系与普通类型的继承关系不同。即使两个类的类型参数有继承关系,它们的泛型类型之间也没有继承关系
-
Box
不是 Box
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· Manus的开源复刻OpenManus初探
· AI 智能体引爆开源社区「GitHub 热点速览」
· C#/.NET/.NET Core技术前沿周刊 | 第 29 期(2025年3.1-3.9)
· 从HTTP原因短语缺失研究HTTP/2和HTTP/3的设计差异