144. Java 泛型 - 对泛型的限制

发布于:2025-07-29 ⋅ 阅读:(15) ⋅ 点赞:(0)

144. Java 泛型 - 对泛型的限制

无法使用基元类型实例化泛型类型

请考虑以下参数化类型:

class Pair<K, V> {
    private K key;
    private V value;

    public Pair(K key, V value) {
        this.key = key;
        this.value = value;
    }
}

创建 Pair 对象时,不能用基元类型(如 intchar)替换类型参数 KV

Pair<int, char> p = new Pair<>(8, 'a');  // 编译错误

只能使用对应的包装类,例如 IntegerCharacter

Pair<Integer, Character> p = new Pair<>(8, 'a');

Java 编译器会自动进行 自动装箱(autoboxing):

Pair<Integer, Character> p = new Pair<>(Integer.valueOf(8), Character.valueOf('a'));

提示:自动装箱的机制可以帮助在基元类型和包装类型之间自动转换。

无法创建类型参数的实例

由于 类型擦除(type erasure),不能直接创建泛型类型参数的实例。例如,以下代码会导致编译错误:

public static <E> void append(List<E> list) {
    E elem = new E();  // 编译错误
    list.add(elem);
}

解决方案:可以使用 反射(Reflection)创建实例:

public static <E> void append(List<E> list, Class<E> cls) throws Exception {
    E elem = cls.getDeclaredConstructor().newInstance();   // OK
    list.add(elem);
}

调用示例:

List<String> ls = new ArrayList<>();
append(ls, String.class);

无法声明类型参数的静态字段

由于 静态变量是类级别的,而类型参数是 实例级别的,因此不允许在静态变量中使用泛型类型参数:

public class MobileDevice<T> {
    private static T os;  // 编译错误
}

这样会导致所有 MobileDevice 实例共享相同的 os 变量,但 T 在不同实例中可能代表不同的类型,造成 类型冲突

不能使用 instanceof 或强制类型转换与参数化类型

由于 类型擦除,泛型类型的参数信息在运行时 不可用,因此不能使用 instanceof 进行类型检查:

public static <E> void rtti(List<E> list) {
    if (list instanceof ArrayList<Integer>) {  // 编译错误
        // ...
    }
}

可以使用 无界通配符 ? 进行 instanceof 检查:

public static void rtti(List<?> list) {
    if (list instanceof ArrayList<?>) {  // OK
        // ...
    }
}

同样,不能进行 不安全的强制类型转换

List<Integer> li = new ArrayList<>();
List<Number> ln = (List<Number>) li;  // 编译错误

但如果泛型类型一致,转换是允许的:

List<String> l1 = new ArrayList<>();
ArrayList<String> l2 = (ArrayList<String>) l1;  // OK

无法创建参数化类型的数组

不能创建 泛型数组,如下代码会导致编译错误:

List<Integer>[] arrayOfLists = new List<Integer>[2];  // 编译错误

原因是数组在运行时存储实际的对象类型,而 泛型的类型参数会被擦除,导致类型信息丢失。

示例问题

Object[] strings = new String[2];
strings[0] = "hi";   // OK
strings[1] = 100;    // ArrayStoreException 异常

如果泛型数组被允许,则可能会导致 运行时错误无法检测

Object[] stringLists = new List<String>[2];  // 假设允许
stringLists[0] = new ArrayList<String>();   // OK
stringLists[1] = new ArrayList<Integer>();  // 无法检测错误

无法创建、捕获或抛出参数化类型的异常

泛型类不能 直接或间接 继承 Throwable,例如:

class MathException<T> extends Exception { }  // 编译错误
class QueueFullException<T> extends Throwable { }  // 编译错误

同样,不能捕获 泛型参数化的异常

public static <T extends Exception, J> void execute(List<J> jobs) {
    try {
        for (J job : jobs) {
            // ...
        }
    } catch (T e) {  // 编译错误
        // ...
    }
}

但是,可以在 throws 子句中使用泛型参数

class Parser<T extends Exception> {
    public void parse(File file) throws T {   // OK
        // ...
    }
}

不能重载类型擦除后签名相同的方法

泛型方法在 类型擦除 后可能会有相同的 方法签名,导致 编译错误

public class Example {
    public void print(Set<String> strSet) { }
    public void print(Set<Integer> intSet) { }  // 编译错误
}

因为 类型擦除 后,方法签名都变成 print(Set),导致重复定义。

解决方案:可以 更改方法名称使用不同的参数类型


以上是 Java 泛型的一些限制和解决方案,希望对你有所帮助!🚀


网站公告

今日签到

点亮在社区的每一天
去签到