返回

Java 泛型:15 个鲜为人知的问题

闲谈

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>