主题
面试速答(先看这里)
**一句话结论:**也就是说,在代码中的Foo<String> 和 Foo<Integer>使用的类,经过编译后都是同一个类。
60秒标准回答:
类型擦除是Java在处理泛型的一种方式,如Java的编译器在编译以下代码时
在编译后的字节码文件中,会把泛型的信息擦除掉
也就是说,在代码中的Foo<String> 和 Foo<Integer>使用的类,经过编译后都是同一个类
**答题顺序:**结论 → 原理/机制 → 关键流程 → 场景与取舍 → 易错点
回答主线:
- **要点1:**类型擦除是Java在处理泛型的一种方式,如Java的编译器在编译以下代码时:
- **要点2:**所以说泛型技术实际上是Java语言的一颗语法糖,因为泛型经过编译器处理之后就被擦除了。
- **要点3:**这种擦除的过程,被称之为——类型擦除。
- **要点4:**类型擦除可以简单的理解为将泛型java代码转换为普通java代码,只不过编译器更直接点,将泛型java代码直接转换成普通java字节码。
- **要点5:**泛型是一种编程范式,在不同的语言和编译器中的实现和支持方式都不一样。
**记忆锚点:**java → Foo → Java → 编译后都是同一个类 → 型擦除指的是通过类 → 的关键在于从泛型类
加分表达:
- 类型擦除的关键在于从泛型类型中清除类型参数的相关信息,并且在必要的时候添加类型检查和类型转换的方法。
追问准备:
- 围绕「java」:底层原理是什么?使用时有哪些边界和常见坑?
- 围绕「Foo」:底层原理是什么?使用时有哪些边界和常见坑?
- 围绕「Java」:底层原理是什么?使用时有哪些边界和常见坑?
- 如果线上出现异常,你会如何定位、验证并规避?
典型回答
类型擦除是Java在处理泛型的一种方式,如Java的编译器在编译以下代码时:
plain
public class Foo<T> {
T bar;
void doSth(T param) {
}
};
Foo<String> f1;
Foo<Integer> f2;在编译后的字节码文件中,会把泛型的信息擦除掉:
plain
public class Foo {
Object bar;
void doSth(Object param) {
}
};也就是说,在代码中的Foo<String> 和 Foo<Integer>使用的类,经过编译后都是同一个类。
所以说泛型技术实际上是Java语言的一颗语法糖,因为泛型经过编译器处理之后就被擦除了。
这种擦除的过程,被称之为——类型擦除。所以类型擦除指的是通过类型参数合并,将泛型类型实例关联到同一份字节码上。编译器只为泛型类型生成一份字节码,并将其实例关联到这份字节码上。类型擦除的关键在于从泛型类型中清除类型参数的相关信息,并且在必要的时候添加类型检查和类型转换的方法。
类型擦除可以简单的理解为将泛型java代码转换为普通java代码,只不过编译器更直接点,将泛型java代码直接转换成普通java字节码。
扩展知识
C语言对泛型的支持
泛型是一种编程范式,在不同的语言和编译器中的实现和支持方式都不一样。
通常情况下,一个编译器处理泛型有多种方式,在C++中,当编译器对以下代码编译时:
plain
template<typename T>
struct Foo
{
T bar;
void doSth(T param) {
}
};
Foo<int> f1;
Foo<float> f2;当编译器对其进行编译时,编译器发现要用到Foo<int>和Foo<float>,这时候就会为每一个泛型类新生成一份执行代码。相当于新创建了如下两个类:
plain
struct FooInt
{
int bar;
void doSth(int param) {
}
};
struct FooFloat
{
float bar;
void doSth(float param) {
}
};这种做法,用起来的时候很方便,只需要根据具体类型找到具体的的类和方法就行了。但是问题是,当我们多次使用不同类型的模板时,就会创建出来的很多新的类,就会导致代码膨胀。