面试题答案
一键面试什么是类型擦除
在Java中,泛型主要是在编译期起作用。类型擦除指的是,当代码编译成字节码后,泛型的类型信息会被擦除,只保留原始类型。例如,List<String>
在编译后,实际的类型是List
,String
类型信息被擦除。编译器会在编译时使用类型参数来进行类型检查和类型推断,但生成的字节码中并不包含泛型类型参数的信息。
对集合框架中泛型使用的影响
- 无法在运行时获取真实类型:由于类型擦除,在运行时无法获取泛型的实际类型。例如:
List<String> list = new ArrayList<>();
// 以下代码编译错误,因为运行时无法获取list的泛型实际类型
if (list instanceof List<String>) {
}
- 可能导致潜在的类型安全问题:如果通过反射等手段绕过编译时的类型检查,可能会向集合中添加错误类型的元素。例如:
List<String> list = new ArrayList<>();
list.add("hello");
try {
Method addMethod = list.getClass().getMethod("add", Object.class);
addMethod.invoke(list, 123); // 绕过泛型检查,运行时不会报错,但后续使用list可能会引发ClassCastException
} catch (Exception e) {
e.printStackTrace();
}
String str = list.get(1); // 运行时抛出ClassCastException
确保集合操作类型安全的方法
- 使用通配符:使用
?
通配符来限制集合元素类型,例如List<? extends Number>
表示该集合元素是Number
及其子类类型。这样在读取集合元素时可以确保类型安全。
List<Number> numbers = new ArrayList<>();
numbers.add(1);
numbers.add(2.5);
List<? extends Number> readOnlyList = numbers;
Number num = readOnlyList.get(0); // 安全
// readOnlyList.add(3); // 编译错误,通配符类型的集合不能添加元素,除了null
- 自定义泛型方法:通过自定义泛型方法来确保类型安全。
public class GenericUtils {
public static <T> void addElement(List<T> list, T element) {
list.add(element);
}
}
List<String> stringList = new ArrayList<>();
GenericUtils.addElement(stringList, "world");
- 使用类型检查和转换:在从集合中获取元素时,进行类型检查和显式转换。虽然这种方式比较繁琐且失去了泛型的部分优势,但在某些情况下是必要的。
List list = new ArrayList();
list.add("hello");
Object obj = list.get(0);
if (obj instanceof String) {
String str = (String) obj;
System.out.println(str.length());
}