Java 泛型:15 个鲜为人知的问题
2023-09-08 18:36:04
1. 什么是 Java 泛型?
Java 泛型是参数化类型,这意味着所操作的数据类型被指定为一个参数(type parameter)。这种参数类型可以用在类、接口和方法的创建中,分别称为泛型类、泛型接口、泛型方法。
2. 使用 Java 泛型的优点是什么?
使用 Java 泛型的优点包括:
- 提高代码的可重用性:泛型类和泛型接口可以被重用,而无需修改代码。
- 提高代码的安全性:泛型可以帮助你避免类型转换错误,从而提高代码的安全性。
- 提高代码的性能:泛型可以帮助你优化代码的性能,因为编译器可以根据泛型参数的类型来优化代码。
3. 如何定义泛型类?
要定义泛型类,你可以在类名后面加上一个尖括号,尖括号里是泛型参数的类型。例如,以下代码定义了一个泛型类 Box
,这个类可以存储任何类型的对象:
public class Box<T> {
private T value;
public Box(T value) {
this.value = value;
}
public T getValue() {
return value;
}
public void setValue(T value) {
this.value = value;
}
}
4. 如何定义泛型接口?
要定义泛型接口,你可以在接口名后面加上一个尖括号,尖括号里是泛型参数的类型。例如,以下代码定义了一个泛型接口 Comparator
,这个接口可以比较两种类型的数据:
public interface Comparator<T> {
int compare(T o1, T o2);
}
5. 如何定义泛型方法?
要定义泛型方法,你可以在方法名后面加上一个尖括号,尖括号里是泛型参数的类型。例如,以下代码定义了一个泛型方法 swap
,这个方法可以交换两种类型的数据:
public static <T> void swap(T[] array, int i, int j) {
T temp = array[i];
array[i] = array[j];
array[j] = temp;
}
6. Java 中的通配符是什么?
Java 中的通配符是 ?
,它可以用来表示任意类型。例如,以下代码定义了一个泛型类 Box
,这个类可以存储任何类型的对象:
public class Box<?> {
private Object value;
public Box(Object value) {
this.value = value;
}
public Object getValue() {
return value;
}
public void setValue(Object value) {
this.value = value;
}
}
7. 泛型类和原始类的区别是什么?
泛型类是使用泛型参数定义的类,原始类是没有使用泛型参数定义的类。例如,以下代码定义了一个泛型类 Box
和一个原始类 Box2
:
public class Box<T> {
private T value;
public Box(T value) {
this.value = value;
}
public T getValue() {
return value;
}
public void setValue(T value) {
this.value = value;
}
}
public class Box2 {
private Object value;
public Box2(Object value) {
this.value = value;
}
public Object getValue() {
return value;
}
public void setValue(Object value) {
this.value = value;
}
}
泛型类可以存储任何类型的对象,而原始类只能存储 Object
类型的对象。
8. 泛型接口和原始接口的区别是什么?
泛型接口是使用泛型参数定义的接口,原始接口是没有使用泛型参数定义的接口。例如,以下代码定义了一个泛型接口 Comparator
和一个原始接口 Comparator2
:
public interface Comparator<T> {
int compare(T o1, T o2);
}
public interface Comparator2 {
int compare(Object o1, Object o2);
}
泛型接口可以比较两种类型的对象,而原始接口只能比较两种 Object
类型的对象。
9. 泛型方法和原始方法的区别是什么?
泛型方法是使用泛型参数定义的方法,原始方法是没有使用泛型参数定义的方法。例如,以下代码定义了一个泛型方法 swap
和一个原始方法 swap2
:
public static <T> void swap(T[] array, int i, int j) {
T temp = array[i];
array[i] = array[j];
array[j] = temp;
}
public static void swap2(Object[] array, int i, int j) {
Object temp = array[i];
array[i] = array[j];
array[j] = temp;
}
泛型方法可以交换两种类型的对象,而原始方法只能交换两种 Object
类型的对象。
10. 如何在 Java 中使用泛型?
要在 Java 中使用泛型,你可以在类名、接口名或方法名后面加上一个尖括号,尖括号里是泛型参数的类型。例如,以下代码创建了一个 Box
对象,这个对象可以存储一个 Integer
类型的值:
Box<Integer> box = new Box<>(10);
11. Java 中的泛型擦除是什么?
Java 中的泛型擦除是指在编译阶段,编译器会将泛型类型参数擦除,只留下原始类型。例如,以下代码编译后,Box
类的类型参数 T
将会被擦除,只剩下 Object
类型:
public class Box<T> {
private T value;
public Box(T value) {
this.value = value;
}
public T getValue() {
return value;
}
public void setValue(T value) {
this.value = value;
}
}
12. 泛型擦除有什么影响?
泛型擦除的主要影响是,它会限制泛型代码的灵活性。例如,以下代码在编译后会报错,因为编译器无法知道 Box
对象中存储的是什么类型的数据:
Box box = new Box(10);
String value = box.getValue();
13. 如何避免泛型擦除的影响?
要避免泛型擦除的影响,你可以使用以下方法:
- 使用通配符:你可以使用通配符
?
来表示任意类型。例如,以下代码就不会报错:
Box<?> box = new Box(10);
Object value = box.getValue();
- 使用反射:你可以使用反射来获取泛型类型参数的实际类型。例如,以下代码可以使用反射来获取
Box
对象中存储的类型:
Box<Integer> box = new Box<>(10);
Class<?> clazz = box.getClass();
Type[] genericInterfaces = clazz.getGenericInterfaces();
Type genericInterface = genericInterfaces[0];
ParameterizedType parameterizedType = (ParameterizedType) genericInterface;
Class<?> actualType = (Class<?>) parameterizedType.getActualTypeArguments()[0];
14. Java 中的泛型类型安全是什么?
Java 中的泛型类型安全是指,编译器会检查泛型类型参数的类型,以确保它们是兼容的。例如,以下代码在编译后会报错,因为 Box
对象中存储的是一个 String
类型的值,而 getValue()
方法返回的是一个 Integer
类型的值:
Box<Integer> box = new Box<>("Hello");
Integer value = box.getValue();
15. 如何提高 Java 中泛型的安全性?
要提高 Java 中泛型的安全性,你可以使用以下方法:
- 使用通配符:你可以使用通配符
?
来表示任意类型。例如,以下代码就不会报错:
Box<?> box = new Box<>("Hello");
Object value = box.getValue();
- 使用反射:你可以使用反射来获取泛型类型参数的实际类型。例如,以下代码可以使用反射来获取
Box
对象中存储的类型:
Box<String>