18.1.7 泛型擦除机制与通配符使用
1. 泛型基础概念
1.1 泛型的定义和作用
泛型是JDK 1.5引入的特性,允许在定义类、接口和方法时使用类型参数,提供编译时类型安全检查。
// 不使用泛型的问题
public class WithoutGenerics {
public void demonstrateProblems() {
List list = new ArrayList();
list.add("字符串");
list.add(123); // 可以添加不同类型
// 运行时类型转换异常
for (Object obj : list) {
String str = (String) obj; // ClassCastException
System.out.println(str);
}
}
}
// 使用泛型解决问题
public class WithGenerics {
public void demonstrateSolution() {
List<String> list = new ArrayList<>();
list.add("字符串");
// list.add(123); // 编译错误,类型不匹配
// 无需类型转换
for (String str : list) {
System.out.println(str);
}
}
}
1.2 泛型的语法形式
// 泛型类
public class GenericClass<T> {
private T data;
public GenericClass(T data) {
this.data = data;
}
public T getData() {
return data;
}
public void setData(T data) {
this.data = data;
}
}
// 泛型接口
public interface GenericInterface<T, R> {
R process(T input);
}
// 泛型方法
public class GenericMethods {
// 静态泛型方法
public static <T> void swap(T[] array, int i, int j) {
T temp = array[i];
array[i] = array[j];
array[j] = temp;
}
// 实例泛型方法
public <T extends Comparable<T>> T findMax(T[] array) {
if (array == null || array.length == 0) {
return null;
}
T max = array[0];
for (int i = 1; i < array.length; i++) {
if (array[i].compareTo(max) > 0) {
max = array[i];
}
}
return max;
}
}
2. 类型擦除机制详解
2.1 类型擦除的概念
类型擦除是Java泛型实现的核心机制,编译器在编译时会移除所有泛型类型信息,在字节码中只保留原始类型。
public class TypeErasureDemo {
public void demonstrateErasure() {
// 编译前
List<String> stringList = new ArrayList<String>();
List<Integer> integerList = new ArrayList<Integer>();
// 编译后(字节码中)
// List stringList = new ArrayList();
// List integerList = new ArrayList();
// 运行时类型相同
System.out.println(stringList.getClass() == integerList.getClass()); // true
System.out.println(stringList.getClass().getName()); // java.util.ArrayList
}
// 泛型类的擦除
public static class GenericClass<T> {
private T value;
public void setValue(T value) {
this.value = value;
}
public T getValue() {
return value;
}
}
public void demonstrateClassErasure() {
GenericClass<String> stringClass = new GenericClass<>();
GenericClass<Integer> integerClass = new GenericClass<>();
// 运行时类型相同
System.out.println(stringClass.getClass() == integerClass.getClass()); // true
}
}
2.2 擦除规则
public class ErasureRules {
// 规则1: 无界类型参数擦除为Object
public class UnboundedGeneric<T> {
private T value; // 擦除后变为 Object value;
public T getValue() { // 擦除后变为 Object getValue()
return value;
}
}
// 规则2: 有界类型参数擦除为第一个边界
public class BoundedGeneric<T extends Number> {
private T value; // 擦除后变为 Number value;
public T getValue() { // 擦除后变为 Number getValue()
return value;
}
}
// 规则3: 多个边界时擦除为第一个边界
public class MultipleBounds<T extends Number & Comparable<T>> {
private T value; // 擦除后变为 Number value;
public T getValue() { // 擦除后变为 Number getValue()
return value;
}
}
// 规则4: 接口作为第一个边界
public class InterfaceFirst<T extends Comparable<T> & Serializable> {
private T value; // 擦除后变为 Comparable value;
public T getValue() { // 擦除后变为 Comparable getValue()
return value;
}
}
}
2.3 桥接方法
public class BridgeMethodDemo {
// 父类
public static class Parent<T> {
public T getValue() {
return null;
}
public void setValue(T value) {
// 设置值
}
}
// 子类
public static class Child extends Parent<String> {
@Override
public String getValue() { // 重写方法
return "Child Value";
}
@Override
public void setValue(String value) { // 重写方法
System.out.println("Setting value: " + value);
}
}
public void demonstrateBridgeMethod() throws Exception {
Child child = new Child();
// 获取所有方法
Method[] methods = Child.class.getDeclaredMethods();
System.out.println("Child类的所有方法:");
for (Method method : methods) {
System.out.printf("方法: %s, 返回类型: %s, 是否为桥接方法: %s%n",
method.getName(),
method.getReturnType().getSimpleName(),
method.isBridge());
}
// 编译器会生成桥接方法:
// public Object getValue() { return getValue(); } // 桥接方法
// public void setValue(Object value) { setValue((String)value); } // 桥接方法
}
public static void main(String[] args) throws Exception {
new BridgeMethodDemo().demonstrateBridgeMethod();
}
}
3. 通配符详解
3.1 通配符的类型
public class WildcardTypes {
// 1. 无界通配符 ?
public void unboundedWildcard() {
List<?> list = new ArrayList<String>();
// list.add("test"); // 编译错误,不能添加元素(除了null)
list.add(null); // 可以添加null
Object obj = list.get(0); // 只能以Object类型读取
System.out.println("读取到: " + obj);
}
// 2. 上界通配符 ? extends T
public void upperBoundedWildcard() {
List<? extends Number> numbers = new ArrayList<Integer>();
// numbers.add(123); // 编译错误,不能添加元素
// numbers.add(12.3); // 编译错误,不能添加元素
numbers.add(null); // 只能添加null
Number num = numbers.get(0); // 可以以Number类型读取
System.out.println("读取到: " + num);
}
// 3. 下界通配符 ? super T
public void lowerBoundedWildcard() {
List<? super Integer> numbers = new ArrayList<Number>();
numbers.add(123); // 可以添加Integer及其子类型
numbers.add(456);
// numbers.add(12.3); // 编译错误,Double不是Integer的子类型
Object obj = numbers.get(0); // 只能以Object类型读取
System.out.println("读取到: " + obj);
}
}
3.2 PECS原则
Producer Extends, Consumer Super - 生产者使用extends,消费者使用super
public class PECSPrinciple {
// Producer - 从集合中读取数据(生产数据给你)
public v
剩余60%内容,订阅专栏后可继续查看/也可单篇购买
Java面试圣经 文章被收录于专栏
Java面试圣经,带你练透java圣经