JAVA泛型相关面试题

Java 泛型的作用是什么? 什么是泛型擦除 ?

泛型允许开发者编写类型参数化的代码,我觉得它最直接的作用: 能够将运行时异常转为编译时异常

引入泛型之前,集合类通常都是Object类型,从集合中获取元素时,必须手动将 Object 显式转换为具体类型。

// 没有使用泛型的情况
List list = new ArrayList();
list.add("Hello");
list.add(123); // 添加了一个 Integer
​
String str = (String) list.get(0); // 这是安全的
String numStr = (String) list.get(1); // 运行时会抛出 ClassCastException

引入泛型之后,编译器可以在编译阶段检查类型,比如,向 List 添加非 String 类型对象,编译器会直接报错。

// 使用泛型的情况
List stringList = new ArrayList<>();
stringList.add("Hello");
​
// 下面这行代码会导致编译错误,因为只能添加 String 类型的对象
// stringList.add(123);
​
String str = stringList.get(0); // 不需要显式类型转换,而且是安全的

其次泛型允许编写类型无关的通用逻辑,比如可以使用无界泛型T,编写一个通用的泛型 Box类,即可支持任意类型的属性

class Box {
    private T value;
    public Box(T value) { this.value = value; }
    public T get() { return value; }
}

泛型擦除

泛型擦除是 Java 泛型的实现机制。编译器在编译阶段移除所有泛型类型信息,替换为原始类型或上界,以确保与 Java旧版本的字节码兼容。

这样一来在运行时无法获取泛型的实际类型。

//无界泛型: 被擦除为 Object
//有界泛型: 被擦除为 Number

既然擦除了类型,为什么在运行期通过反射可以获得类型?

泛型信息会存在字节码元数据中,所以在运行期通过反射可以获得类型

泛型的使用方式有哪几种?

泛型一般有三种使用方式:泛型类泛型接口泛型方法

泛型类

//此处T可以随便写为任意标识,常见的如T、E、K、V等形式的参数常用于表示泛型
//在实例化泛型类时,必须指定T的具体类型
public class Generic{
    private T key;
    public T getKey(){
        return key;
    }
}
Generic genericInteger = new Generic(123456);

泛型接口

public interface Generator {
    public T method();
}
//实现泛型接口,不指定类型
class GeneratorImpl implements Generator{
    @Override
    public T method() {
        return null;
    }
}
​
//实现泛型接口,指定类型
class GeneratorImpl implements Generator {
    @Override
    public String method() {
        return "hello";
    }
}

什么是Java泛型的上下限定符?

上界限定符

表示类型的上界, ? 这个类型要么是T,要么是T的子类

通常用于读取操作,确保可以读取为 T或T的子类的对象。

// 定义一个泛型方法,接受任何继承自Number的类型
public  void processNumber(T number) {
    
    double value = number.doubleValue();  //因为 number可以是任何Number类型或其子类型,所以可以使用Number的方法
    // 其他操作...
}
下界限定符

表示类型的下界,? 这个类型要么是T,要么是 T 的父类型,直至 Object

通常用于写入操作,确保可以安全地向泛型集合中插入 T 类型的对象

// 定义一个泛型方法,接受任何类型的List,并向其中添加元素
public  void addElements(List list, T element) {  //表明列表的参数类型至少是T的父类
    list.add(element);
    // 其他操作...
}

我们在使用上下界通配符的时候,需要遵循 pecs 原则,即上界生产,下界消费。

PECS原则

  • Producer Extends : 从一个容器中获取数据时,应该使用 ? extends T,确保读取的是T或其子类型

  • Consumer Super : 向一个容器中写入数据时,应该使用 ? super T,确保写入的是T或其子类型

你可能感兴趣的:(java,windows,开发语言)