【Java基础】泛型:泛型擦除、通配符、上下界限定(附《思维导图》+《面试高频考点清单》)

文章目录

Java基础:泛型核心知识体系(泛型擦除、通配符、上下界限定)

一、泛型概述

1.1 定义与本质

泛型(Generics)是JDK 5.0引入的参数化类型特性,允许在定义类、接口、方法时使用类型参数(Type Parameter),这些类型参数在使用时才被指定为具体类型。

本质:类型安全的"模板机制",将数据类型作为参数传递,实现代码复用与类型安全的统一。

1.2 核心优势

  • 类型安全:编译期类型检查,避免运行时ClassCastException
  • 代码复用:一套逻辑可处理多种数据类型
  • 可读性:代码意图更清晰,无需频繁强制类型转换
  • 解耦:数据类型与业务逻辑分离

1.3 泛型的三种使用形式

使用形式 语法示例 说明
泛型类 class Box<T> { ... } 类上定义类型参数
泛型接口 interface List<E> { ... } 接口上定义类型参数
泛型方法 <T> T getValue(Box<T> box) { ... } 方法上独立定义类型参数

二、泛型擦除(Type Erasure)

2.1 核心原理

泛型擦除是Java泛型实现的基石 :泛型信息仅存在于编译期,编译完成后所有泛型类型参数都会被擦除,替换为它们的限定类型(无界则为Object)。

设计目的:向后兼容JDK 1.4及之前的版本,保证泛型代码能在旧版JVM上运行。

2.2 擦除规则

  1. 无界类型参数 :擦除为Object

    java 复制代码
    // 编译前
    class Box<T> {
        private T value;
        public T getValue() { return value; }
    }
    
    // 编译后(擦除结果)
    class Box {
        private Object value;
        public Object getValue() { return value; }
    }
  2. 有界类型参数 :擦除为第一个边界类型

    java 复制代码
    // 编译前
    class NumberBox<T extends Number> {
        private T value;
    }
    
    // 编译后
    class NumberBox {
        private Number value;
    }
  3. 多边界类型参数:擦除为第一个边界类型

    java 复制代码
    // 编译前
    class ComparableBox<T extends Number & Comparable<T>> {
        private T value;
    }
    
    // 编译后
    class ComparableBox {
        private Number value;
    }
  4. 泛型方法:同样遵循上述擦除规则

    java 复制代码
    // 编译前
    public static <T extends Comparable<T>> int compare(T a, T b) {
        return a.compareTo(b);
    }
    
    // 编译后
    public static int compare(Comparable a, Comparable b) {
        return a.compareTo(b);
    }

2.3 擦除带来的问题与解决方案

问题1:无法使用instanceof判断泛型类型
java 复制代码
// 编译错误!泛型类型被擦除,运行时无法获取
if (obj instanceof List<String>) { ... }

// 只能判断原始类型
if (obj instanceof List) { ... }

解决方案:使用Class对象作为类型令牌(Type Token)

java 复制代码
public <T> boolean isInstance(Object obj, Class<T> clazz) {
    return clazz.isInstance(obj);
}
问题2:无法创建泛型数组
java 复制代码
// 编译错误!泛型类型被擦除,无法保证数组类型安全
List<String>[] lists = new List<String>[10];

// 允许创建原始类型数组,但不安全
List[] lists = new List[10];

解决方案:使用ArrayList代替泛型数组,或使用反射创建

java 复制代码
// 推荐方案
List<List<String>> lists = new ArrayList<>();

// 反射方案(谨慎使用)
List<String>[] lists = (List<String>[]) Array.newInstance(List.class, 10);
问题3:泛型类型参数不能是基本数据类型
java 复制代码
// 编译错误!基本类型不能作为泛型参数
List<int> list = new ArrayList<>();

// 必须使用包装类
List<Integer> list = new ArrayList<>();

原因:擦除后类型变为Object,而基本类型不能赋值给Object变量。

问题4:泛型类的静态变量与静态方法不能使用类型参数
java 复制代码
class Box<T> {
    // 编译错误!静态变量属于类,所有实例共享
    private static T value;
    
    // 编译错误!静态方法不能使用类的类型参数
    public static T getValue() { ... }
    
    // 正确!泛型方法可以有自己的类型参数
    public static <E> E getElement(E element) { ... }
}
问题5:桥接方法(Bridge Method)的生成

当泛型类实现泛型接口或继承泛型父类时,编译器会自动生成桥接方法以保证多态性。

java 复制代码
// 泛型接口
interface Comparable<T> {
    int compareTo(T o);
}

// 实现类
class MyInteger implements Comparable<MyInteger> {
    @Override
    public int compareTo(MyInteger o) {
        return 0;
    }
}

// 编译后生成的桥接方法
class MyInteger implements Comparable {
    public int compareTo(MyInteger o) { ... }
    
    // 桥接方法,保证多态
    public int compareTo(Object o) {
        return compareTo((MyInteger) o);
    }
}

2.4 泛型擦除的本质

Java泛型是**"伪泛型"**,运行时JVM并不知道泛型的存在,所有泛型操作都在编译期完成。这与C++的模板(编译期生成不同类型的类)有本质区别。

三、泛型通配符(Wildcard)

3.1 为什么需要通配符?

泛型不支持协变(Covariance)和逆变(Contravariance),即List<String>不是List<Object>的子类。

java 复制代码
// 编译错误!泛型不协变
List<Object> list = new ArrayList<String>();

通配符 ?的引入就是为了解决泛型的灵活性问题,允许在不破坏类型安全的前提下,实现泛型类型的多态。

3.2 三种通配符类型

1. 无界通配符(Unbounded Wildcard):?
  • 语法:List<?>
  • 含义:表示任意类型的泛型
  • 适用场景:当方法不依赖于泛型的具体类型时
java 复制代码
// 打印任意类型的List
public static void printList(List<?> list) {
    for (Object obj : list) {
        System.out.println(obj);
    }
}

// 可以传入任何类型的List
printList(new ArrayList<String>());
printList(new ArrayList<Integer>());

注意List<?>不能添加任何元素(除了null),因为编译器无法确定具体类型。

java 复制代码
List<?> list = new ArrayList<String>();
list.add(null); // 允许
list.add("hello"); // 编译错误!
2. 上界通配符(Upper Bounded Wildcard):? extends T
  • 语法:List<? extends Number>
  • 含义:表示T或T的子类的泛型
  • 适用场景:读取数据(生产者场景)
java 复制代码
// 计算Number及其子类List的总和
public static double sum(List<? extends Number> list) {
    double sum = 0.0;
    for (Number num : list) {
        sum += num.doubleValue();
    }
    return sum;
}

// 可以传入Number及其子类的List
sum(new ArrayList<Integer>());
sum(new ArrayList<Double>());
sum(new ArrayList<Long>());

特性

  • 可以安全地读取T类型的数据
  • 不能添加任何元素(除了null),因为编译器无法确定具体的子类类型
java 复制代码
List<? extends Number> list = new ArrayList<Integer>();
Number num = list.get(0); // 允许
list.add(123); // 编译错误!
3. 下界通配符(Lower Bounded Wildcard):? super T
  • 语法:List<? super Integer>
  • 含义:表示T或T的父类的泛型
  • 适用场景:写入数据(消费者场景)
java 复制代码
// 向List中添加Integer及其子类
public static void addIntegers(List<? super Integer> list) {
    list.add(123);
    list.add(456);
}

// 可以传入Integer及其父类的List
addIntegers(new ArrayList<Integer>());
addIntegers(new ArrayList<Number>());
addIntegers(new ArrayList<Object>());

特性

  • 可以安全地添加T及其子类类型的数据
  • 读取数据时只能得到Object类型,因为编译器无法确定具体的父类类型
java 复制代码
List<? super Integer> list = new ArrayList<Number>();
list.add(123); // 允许
Object obj = list.get(0); // 只能得到Object
Integer num = list.get(0); // 编译错误!

四、上下界限定的深入理解

4.1 PECS原则(最重要的设计原则)

PECS = Producer Extends, Consumer Super

  • 生产者(Producer) :提供数据的对象,使用? extends T(上界通配符)
  • 消费者(Consumer) :消费数据的对象,使用? super T(下界通配符)

记忆口诀:"读用extends,写用super,读写都不用通配符"

java 复制代码
// 生产者:从src中读取数据,src是生产者
public static <T> void copy(List<? extends T> src, List<? super T> dest) {
    for (T t : src) {
        dest.add(t);
    }
}

// 使用示例
List<Integer> src = Arrays.asList(1, 2, 3);
List<Number> dest = new ArrayList<>();
copy(src, dest); // 正确

4.2 上下界限定的使用场景对比

通配符类型 语法 允许的类型 读取操作 写入操作 适用场景
无界通配符 ? 任意类型 只能读Object 只能写null 不依赖具体类型的操作
上界通配符 ? extends T T及其子类 可读T 只能写null 生产者(读取数据)
下界通配符 ? super T T及其父类 只能读Object 可写T及其子类 消费者(写入数据)
无通配符 T 具体类型T 可读T 可写T 同时读写数据

4.3 上下界限定的注意事项

  1. 通配符不能用于定义泛型类/接口/方法

    java 复制代码
    // 编译错误!通配符不能作为类型参数
    class Box<?> { ... }
    
    // 正确!使用类型参数T
    class Box<T> { ... }
  2. 不能同时使用上下界

    java 复制代码
    // 编译错误!不能同时使用extends和super
    List<? extends Number super Integer> list;
  3. 上界通配符的多边界

    java 复制代码
    // 正确!上界可以有多个,用&分隔
    List<? extends Number & Comparable<?>> list;
  4. 下界通配符不能有多个边界

    java 复制代码
    // 编译错误!下界不能有多个边界
    List<? super Integer & Number> list;
  5. 通配符与泛型方法的选择

    • 如果方法的参数之间有依赖关系(如返回值类型依赖于参数类型),使用泛型方法
    • 如果方法的参数之间没有依赖关系,使用通配符更简洁
java 复制代码
// 泛型方法:参数和返回值有依赖关系
public static <T> T getFirst(List<T> list) {
    return list.get(0);
}

// 通配符:参数之间无依赖关系
public static void printFirst(List<?> list) {
    System.out.println(list.get(0));
}

五、常见面试考点与易错点

5.1 核心面试考点

  1. 什么是泛型擦除?为什么Java要使用泛型擦除?
  2. 泛型擦除带来了哪些问题?如何解决?
  3. 什么是桥接方法?为什么需要桥接方法?
  4. 解释PECS原则,并举例说明。
  5. List<Object>List<?>List<? extends Object>有什么区别?
  6. 为什么不能创建泛型数组?
  7. 泛型方法与通配符的区别与联系?
  8. 泛型在集合框架中的应用(如List、Map)?

5.2 常见易错点

  1. 混淆泛型的协变与数组的协变

    java 复制代码
    // 数组是协变的,允许但不安全
    Object[] arr = new String[10];
    arr[0] = 123; // 运行时ArrayStoreException
    
    // 泛型不协变,编译期就报错
    List<Object> list = new ArrayList<String>(); // 编译错误
  2. 错误地使用通配符进行读写操作

    java 复制代码
    // 错误:上界通配符不能写入
    List<? extends Number> list = new ArrayList<Integer>();
    list.add(123); // 编译错误
    
    // 错误:下界通配符不能安全读取
    List<? super Integer> list = new ArrayList<Number>();
    Integer num = list.get(0); // 编译错误
  3. 在泛型类中使用静态类型参数

    java 复制代码
    class Box<T> {
        // 错误:静态变量不能使用类的类型参数
        private static T value;
    }
  4. 使用instanceof判断泛型类型

    java 复制代码
    // 错误:泛型类型被擦除
    if (obj instanceof List<String>) { ... }
    
    // 正确:只能判断原始类型
    if (obj instanceof List) { ... }
  5. 创建泛型数组

    java 复制代码
    // 错误:不能创建泛型数组
    List<String>[] lists = new List<String>[10];
    
    // 正确:使用List的List
    List<List<String>> lists = new ArrayList<>();

六、实战应用示例

6.1 实现一个通用的工具类

java 复制代码
public class CollectionUtils {
    // 生产者:获取集合中的最大值
    public static <T extends Comparable<? super T>> T max(List<? extends T> list) {
        if (list == null || list.isEmpty()) {
            throw new IllegalArgumentException("List is null or empty");
        }
        
        T max = list.get(0);
        for (T t : list) {
            if (t.compareTo(max) > 0) {
                max = t;
            }
        }
        return max;
    }
    
    // 消费者:将数组元素添加到集合中
    public static <T> void addAll(List<? super T> list, T[] array) {
        for (T t : array) {
            list.add(t);
        }
    }
    
    // 无界通配符:判断集合是否包含指定元素
    public static boolean contains(List<?> list, Object obj) {
        return list.contains(obj);
    }
}

6.2 使用示例

java 复制代码
public class Main {
    public static void main(String[] args) {
        List<Integer> intList = Arrays.asList(1, 3, 5, 2, 4);
        System.out.println("Max: " + CollectionUtils.max(intList)); // 5
        
        List<Number> numList = new ArrayList<>();
        Integer[] intArray = {1, 2, 3};
        CollectionUtils.addAll(numList, intArray);
        System.out.println("numList: " + numList); // [1, 2, 3]
        
        List<String> strList = Arrays.asList("a", "b", "c");
        System.out.println("Contains 'b': " + CollectionUtils.contains(strList, "b")); // true
    }
}

七、知识体系总结

Java泛型的核心是编译期类型检查运行期擦除的结合。泛型擦除保证了向后兼容性,但也带来了一些限制;通配符和上下界限定则在不破坏类型安全的前提下,极大地提高了泛型的灵活性。

掌握PECS原则是正确使用通配符的关键:生产者使用extends,消费者使用super。在实际开发中,应根据具体场景选择合适的泛型形式,以实现代码的复用性、可读性和类型安全。


Java泛型面试核心考点清单(按面试频率排序)

一、最高频考点(★★★★★)

1. 泛型擦除

  • 核心定义 :Java泛型是编译期特性 ,所有泛型类型参数在编译完成后都会被擦除,替换为它们的限定类型(无界则为Object)。运行时JVM并不知道泛型的存在。

  • 设计目的向后兼容JDK 1.4及之前版本,保证泛型代码能在旧版JVM上运行。

  • 擦除规则

    1. 无界类型参数 → 擦除为Object
    2. 有界类型参数 → 擦除为第一个边界类型
    3. 多边界类型参数 → 擦除为第一个边界类型
    4. 泛型方法 → 遵循上述相同规则
  • 带来的5大核心问题及解决方案

    问题 解决方案
    无法使用instanceof判断泛型类型 使用Class对象作为类型令牌(Type Token)
    无法创建泛型数组 使用ArrayList<List<T>>代替,或反射Array.newInstance()
    泛型参数不能是基本数据类型 使用对应的包装类
    泛型类静态成员不能使用类型参数 静态方法使用独立的泛型方法类型参数
    多态性被破坏 编译器自动生成桥接方法(Bridge Method)
  • 桥接方法:当泛型类实现泛型接口或继承泛型父类时,编译器生成的合成方法,用于保证擦除后多态性依然成立。

2. PECS原则

  • 全称:Producer Extends, Consumer Super
  • 核心含义
    • 生产者(提供数据) :使用? extends T(上界通配符),只能读不能写(除null)
    • 消费者(消费数据) :使用? super T(下界通配符),只能写不能安全读(只能读Object)
  • 记忆口诀读用extends,写用super,读写都不用通配符
  • 经典示例Collections.copy(List<? super T> dest, List<? extends T> src)

3. 三种通配符的区别与使用场景

通配符类型 语法 允许的类型 读取能力 写入能力 适用场景
无界通配符 ? 任意类型 只能读Object 只能写null 不依赖具体类型的操作(如打印、判断包含)
上界通配符 ? extends T T及其所有子类 可读T类型 只能写null 生产者场景(从集合中读取数据)
下界通配符 ? super T T及其所有父类 只能读Object 可写T及其子类 消费者场景(向集合中写入数据)

二、高频考点(★★★★)

1. 泛型的本质与核心优势

  • 本质参数化类型,将数据类型作为参数传递,实现"类型安全的模板机制"
  • 核心优势
    1. 编译期类型安全 :避免运行时ClassCastException
    2. 代码复用:一套逻辑处理多种数据类型
    3. 提高可读性:无需频繁强制类型转换
    4. 解耦:数据类型与业务逻辑分离

2. 泛型的三种使用形式

  • 泛型类class Box<T> { ... }(类上定义类型参数)
  • 泛型接口interface List<E> { ... }(接口上定义类型参数)
  • 泛型方法<T> T getValue(Box<T> box) { ... }(方法上独立定义类型参数,与类的泛型无关)

3. List<Object>List<?>List<? extends Object>的区别

  • List<Object>:只能存储Object类型,不能接收List<String>等子类泛型
  • List<?>:可以接收任意类型的List,但只能读Object,只能写null
  • List<? extends Object>:与List<?>完全等价,写法更明确

4. 数组协变 vs 泛型不变

  • 数组是协变的String[]Object[]的子类,但不安全(运行时ArrayStoreException
  • 泛型是不变的List<String>不是List<Object>的子类,编译期就报错,更安全

三、中频考点(★★★)

1. 泛型方法与通配符的选择

  • 使用泛型方法 :当方法参数之间或参数与返回值之间有类型依赖关系
  • 使用通配符 :当方法参数之间没有类型依赖关系时,代码更简洁

2. 上下界限定的注意事项

  • 通配符不能用于定义泛型类/接口/方法(只能用T等类型参数)
  • 不能同时使用上下界(? extends T super U 编译错误)
  • 上界通配符支持多边界(? extends T & U),下界通配符不支持
  • 泛型方法的类型参数也可以有上下界(<T extends Number>

3. 为什么不能创建泛型数组?

  • 泛型擦除后,数组的类型信息丢失,无法保证运行时类型安全
  • 数组是协变的,而泛型是不变的,两者结合会导致类型安全漏洞

四、低频考点(★★)

1. 泛型在集合框架中的应用

  • 所有Java集合类都使用了泛型(List<E>Set<E>Map<K,V>等)
  • 集合的工具类Collections大量使用了泛型和PECS原则

2. 类型令牌(Type Token)的使用

  • 用于在运行时获取泛型类型信息
  • 示例:public <T> T getBean(String name, Class<T> clazz) { ... }

3. 泛型的继承关系

  • 泛型类的继承:class Child<T> extends Parent<T> { ... }
  • 泛型接口的实现:class MyList<E> implements List<E> { ... }

五、常见面试陷阱(★★★★★)

  1. ❌ 错误:if (obj instanceof List<String>) { ... }

    ✅ 正确:if (obj instanceof List) { ... }

  2. ❌ 错误:List<String>[] lists = new List<String>[10];

    ✅ 正确:List<List<String>> lists = new ArrayList<>();

  3. ❌ 错误:List<? extends Number> list = new ArrayList<>(); list.add(123);

    ✅ 正确:List<Number> list = new ArrayList<>(); list.add(123);

  4. ❌ 错误:List<? super Integer> list = new ArrayList<>(); Integer num = list.get(0);

    ✅ 正确:Object obj = list.get(0);

  5. ❌ 错误:class Box<T> { private static T value; }

    ✅ 正确:class Box<T> { private T value; }

六、面试答题技巧

  1. 回答泛型擦除问题时,一定要提到"向后兼容",这是设计的根本原因
  2. 回答通配符问题时,必须结合PECS原则,并给出具体的读写示例
  3. 遇到对比题(如数组协变vs泛型不变),先讲定义,再讲区别,最后讲安全性
  4. 遇到代码题,先指出错误,再说明原因,最后给出正确写法

Java泛型高频面试题+标准答案速记版

(按面试出现频率排序,每题答案控制在30秒-1分钟内,适合考前突击背诵)

一、必考题(100%会问)

Q1:什么是泛型擦除?为什么Java要使用泛型擦除?

标准答案

泛型擦除是Java泛型的实现机制:泛型信息仅存在于编译期,编译完成后所有类型参数都会被擦除,替换为它们的限定类型(无界则为Object),运行时JVM并不知道泛型的存在。

设计目的向后兼容JDK 1.4及更早版本,保证泛型代码能在旧版JVM上无缝运行,这是Java泛型最核心的设计取舍。


Q2:解释PECS原则,并举例说明。

标准答案

PECS = Producer Extends, Consumer Super,是通配符使用的黄金法则:

  • 生产者(提供数据) :使用? extends T(上界通配符),只能安全读取T类型数据,不能写入(除null)
  • 消费者(消费数据) :使用? super T(下界通配符),只能安全写入T及其子类数据,读取只能得到Object

经典示例Collections.copy(dest, src)方法,src是生产者用? extends Tdest是消费者用? super T

记忆口诀:读用extends,写用super,读写都不用通配符。


Q3:List<Object>List<?>List<? extends Object>有什么区别?

标准答案

  1. List<Object> :只能存储Object类型,不支持协变 ,不能接收List<String>等任何子类泛型
  2. List<?> :无界通配符,可以接收任意类型的List,但只能读Object,只能写null
  3. List<? extends Object> :与List<?>完全等价,只是写法更明确,表达"所有继承自Object的类型"

Q4:为什么不能创建泛型数组?

标准答案

两个核心原因:

  1. 类型安全问题:泛型擦除后数组类型信息丢失,无法保证运行时类型安全
  2. 协变与不变的冲突 :数组是协变的String[]Object[]的子类),而泛型是不变的List<String>不是List<Object>的子类),两者结合会产生严重的类型安全漏洞

正确替代方案 :使用ArrayList<List<T>>代替泛型数组。

二、高频题(80%会问)

Q5:泛型的本质是什么?有什么核心优势?

标准答案
本质参数化类型,将数据类型作为参数传递,实现"类型安全的模板机制"。

核心优势

  1. 编译期类型安全 :提前发现类型错误,避免运行时ClassCastException
  2. 代码复用:一套逻辑可处理多种数据类型
  3. 提高可读性:无需频繁强制类型转换
  4. 解耦:数据类型与业务逻辑分离

Q6:什么是桥接方法?为什么需要桥接方法?

标准答案

桥接方法是编译器在泛型类实现泛型接口或继承泛型父类时,自动生成的合成方法

作用 :解决泛型擦除后多态性被破坏的问题。例如:

java 复制代码
// 接口擦除后
interface Comparable { int compareTo(Object o); }

// 实现类
class MyInteger implements Comparable<MyInteger> {
    public int compareTo(MyInteger o) { ... }
    // 编译器自动生成的桥接方法
    public int compareTo(Object o) { return compareTo((MyInteger)o); }
}

Q7:数组协变和泛型不变有什么区别?哪个更安全?

标准答案

  • 数组协变String[]Object[]的子类,允许赋值,但不安全 ,会在运行时抛出ArrayStoreException
  • 泛型不变List<String>不是List<Object>的子类,编译期就报错,更安全

结论:泛型不变比数组协变更安全,这也是Java泛型设计为不变的重要原因。


Q8:泛型方法和通配符有什么区别?什么时候用哪个?

标准答案

  • 泛型方法 :使用<T>定义独立的类型参数,适用于方法参数之间或参数与返回值之间有类型依赖关系的场景
  • 通配符 :使用?表示未知类型,适用于方法参数之间没有类型依赖关系的场景,代码更简洁

示例

  • 泛型方法:public <T> T getFirst(List<T> list)(返回值依赖参数类型)
  • 通配符:public void printFirst(List<?> list)(无类型依赖)

三、中频题(50%会问)

Q9:泛型擦除带来了哪些问题?如何解决?

标准答案

问题 解决方案
无法使用instanceof判断泛型类型 使用Class对象作为类型令牌
无法创建泛型数组 使用ArrayList<List<T>>代替
泛型参数不能是基本数据类型 使用对应的包装类
泛型类静态成员不能使用类型参数 静态方法使用独立的泛型方法类型参数
多态性被破坏 编译器自动生成桥接方法

Q10:为什么不能用instanceof判断泛型类型?

标准答案

因为泛型擦除后,运行时JVM只知道原始类型,不知道具体的泛型参数类型。例如List<String>List<Integer>在运行时都是List类型,无法区分。

正确写法 :只能判断原始类型if (obj instanceof List)


Q11:泛型有哪三种使用形式?各举一个例子。

标准答案

  1. 泛型类class Box<T> { private T value; }
  2. 泛型接口interface List<E> { boolean add(E e); }
  3. 泛型方法public static <T> T getValue(Box<T> box) { return box.value; }

四、陷阱题(容易答错)

Q12:List<? extends Number>可以添加Integer吗?为什么?

标准答案
不可以 。因为List<? extends Number>可以指向List<Integer>List<Double>等任意Number子类的List。如果允许添加Integer,那么当它指向List<Double>时就会出现类型错误。

唯一可以添加的元素是null,因为null是所有引用类型的实例。


Q13:List<? super Integer>可以读取Integer吗?为什么?

标准答案
不可以安全读取 。因为List<? super Integer>可以指向List<Integer>List<Number>List<Object>等任意Integer父类的List。读取时编译器无法确定具体类型,只能返回Object。

正确写法Object obj = list.get(0);


Q14:泛型类的静态方法可以使用类的类型参数吗?为什么?

标准答案
不可以。因为静态方法属于类本身,而泛型类的类型参数是在创建实例时才确定的。静态方法在实例创建前就可以调用,此时类型参数还没有被指定。

正确写法 :静态方法可以定义自己的泛型类型参数:public static <T> T getElement(T element)

五、面试答题加分技巧

  1. 回答任何泛型问题时,只要涉及擦除,一定要提到"向后兼容",这是Java泛型所有设计取舍的根源
  2. 回答通配符问题时,必须结合PECS原则,并给出具体的读写示例
  3. 遇到代码题,先指出错误,再说明原因,最后给出正确写法
  4. 回答对比题时,先讲定义,再讲区别,最后讲安全性
相关推荐
来恩10032 小时前
请求转发与响应重定向的使用
java
ychqsq2 小时前
20.面试
经验分享·职场和发展
暗冰ཏོ2 小时前
VUE面试题大全
前端·javascript·vue.js·面试
@杰克成2 小时前
Java学习30
java·开发语言·学习
次元工程师!2 小时前
LangFlow开发(三)—Bundles组件架构设计(3W+字详细讲解)
java·前端·python·低代码·langflow
凯瑟琳.奥古斯特3 小时前
数据冗余与规范化的本质[数据库原理]
开发语言·数据库·职场和发展
落羽的落羽3 小时前
【算法札记】练习 | Week4
linux·服务器·数据结构·c++·人工智能·算法·动态规划
萑澈4 小时前
算法竞赛入门:C++ STL核心用法与时空复杂度速查手册
数据结构·c++·算法·stl
oddsand14 小时前
Redis网络模型
java·数据库·redis