Java 泛型
Java 泛型(generics)是 JDK 5 中引入的一个新特性, 泛型提供了编译时类型安全检测机制,该机制允许程序员在编译时检测到非法的类型。
泛型的本质是参数化类型,也就是说所操作的数据类型被指定为一个参数。
假定我们有这样一个需求:写一个排序方法,能够对整型数组、字符串数组甚至其他任何类型的数组进行排序,该如何实现? 答案是可以使用 Java 泛型。 使用 Java 泛型的概念,我们可以写一个泛型方法来对一个对象数组排序。然后,调用该泛型方法来对整型数组、浮点数数组、字符串数组等进行排序。
Java泛型(Generics)是一种编程语言功能,它允许在编写代码时定义可重用的类和接口,而不必为每种可能的类型编写重复的代码。泛型可以提高代码的可读性、重用性和类型安全性。
Java泛型的主要概念
类型参数:泛型类或接口使用尖括号
<>
内的类型参数表示其通用类型。例如,
List<T>
中的T
就是一个类型参数,表示该列表可以存储任何类型的元素。类型参数通常用大写字母表示,常见的有T
(Type)、E
(Element)、K
(Key)和V
(Value)。泛型类和接口:泛型类和接口在定义时使用类型参数,用于指定成员变量、方法参数和返回值的类型。
例如,Java集合框架中的
List<E>
接口和ArrayList<E>
类就是泛型接口和类的示例。public interface List<E> {
void add(E e);
E get(int index);
}
public class ArrayList<E> implements List<E> {
// 实现细节
}实例化泛型类型:当创建泛型类或接口的实例时,需要为类型参数指定具体的类型。
例如,
List<String>
表示一个包含字符串元素的列表,List<Integer>
表示一个包含整数元素的列表。List<String> stringList = new ArrayList<String>();
List<Integer> integerList = new ArrayList<Integer>();从Java 7开始,可以使用菱形操作符
<>
省略构造函数中的类型参数,编译器会自动推断出正确的类型。List<String> stringList = new ArrayList<>();
List<Integer> integerList = new ArrayList<>();类型擦除:为了保持与没有泛型的旧代码的兼容性,Java泛型在编译时会被擦除。
这意味着泛型类型信息在运行时不可用。例如,在运行时,
List<String>
和List<Integer>
都被当作List
来处理。这有时会导致一些限制,如不能直接创建泛型数组。有界类型参数:可以使用
extends
关键字限制泛型类型参数的取值范围。例如,
<T extends Comparable<T>>
表示类型参数T
必须实现Comparable<T>
接口。public class SortedPair<T extends Comparable<T>> {
private final T first;
private final T second;
public SortedPair(T first, T second) {
if (first.compareTo(second) <= 0) {
this.first = first;
this.second = second;
} else {
this.first = second;
this.second = first;
}
}
// 其他方法
}通配符:通配符
?
可以用于表示未知的泛型类型。它通常用于泛型方法的参数类型,以实现更灵活的多态性。通配符可以与
extends
和super
关键字结合使用,表示上界和下界。// 使用通配符的泛型方法
public static void printList(List<?> list) {
for (Object item : list) {
System.out.println(item);
}
}
java 中泛型标记符:
- E - Element (在集合中使用,因为集合中存放的是元素)
- T - Type(Java 类)
- K - Key(键)
- V - Value(值)
- N - Number(数值类型)
- ? - 表示不确定的 java 类型
如何理解泛型
刚开始学习Java时,理解泛型可能有些困难。但不用担心,可以通过以下简化的方法来逐步理解Java泛型:
泛型的目的:泛型的主要目的是提高代码的重用性和类型安全性。
当需要为多种类型创建相似的类或方法时,泛型可以帮助你避免编写重复代码。
泛型类和泛型接口:泛型类和泛型接口使用类型参数(如
T
、E
等)来表示通用类型。这允许我们在实例化这些类或接口时为其指定具体的类型。例如,Java集合框架中的
List<E>
就是一个泛型接口。public interface List<E> {
void add(E e);
E get(int index);
}实例化泛型类型:当你创建泛型类或接口的实例时,需要为类型参数指定具体的类型。
例如,
List<String>
表示一个包含字符串元素的列表,List<Integer>
表示一个包含整数元素的列表。List<String> stringList = new ArrayList<String>();
List<Integer> integerList = new ArrayList<Integer>();这样,你可以在编译时获得更好的类型检查,避免运行时出现类型错误。
通配符:通配符
?
用于表示未知的泛型类型,可以让你编写更灵活的代码。通常情况下,通配符用于泛型方法的参数类型。例如,以下方法可以打印任何类型的列表:
public static void printList(List<?> list) {
for (Object item : list) {
System.out.println(item);
}
}
随着对Java编程的熟练度提高,将更好地理解泛型的概念和应用。在实际编程过程中,多尝试使用泛型并阅读相关文档,有助于加深对泛型的理解。
泛型方法和泛型类有什么区别
泛型方法和泛型类都是Java泛型的重要组成部分,它们的主要区别在于应用范围和定义方式:
泛型类:
应用范围:泛型类中的类型参数可以在整个类中使用,包括类的属性、方法参数和方法返回类型。
定义方式:在类名后面添加尖括号
<>
,并在括号内指定类型参数。类型参数通常用单个大写字母表示,如T
、E
等。例如,一个简单的泛型类
Box
:public class Box<T> {
private T content;
public T getContent() {
return content;
}
public void setContent(T content) {
this.content = content;
}
}
泛型方法:
应用范围:泛型方法中的类型参数仅在该方法内有效,不能在其他方法中使用。
定义方式:在方法返回类型之前添加尖括号
<>
,并在括号内指定类型参数。类型参数通常用单个大写字母表示,如T
、E
等。例如,一个简单的泛型方法
printArray
:public class Utils {
public static <T> void printArray(T[] array) {
for (T item : array) {
System.out.println(item);
}
}
}
总结一下,泛型类允许你在整个类中使用类型参数,而泛型方法则限制在特定方法内。在实际编程中,你可能会遇到泛型类与泛型方法结合使用的情况,以实现更高级别的泛型编程。