02 反射 泛型(II)

news2025/4/5 10:52:39

目录

一、反射

1. 反射引入

2. 创建对象

 3. 反射核心用法

二、泛型

1. 泛型的重要性

(1)解决类型安全问题

(2)避免重复代码

(3)提高可读性和维护性

2. 泛型用法

(1)泛型类

(2)泛型方法

(3)泛型类派生子类

(4)类型通配符

3. 泛型擦除

(1)为什么需要泛型擦除

(2)泛型擦除带来的限制

4. 桥接方法

(1)问题

(2)桥接方法的作用


一、反射

1. 反射引入

MyClass obj = new MyClass();
obj.doSth();

类加载过程:

(1)加载 Loading

JVM将类的字节码文件(.class)加载到内存,创建Class对象

(2)链接 Linking

① 验证:确保字节码符合规范

② 准备:为静态变量分配内存并赋予默认值(如int初始化为0)

③ 解析:将符号引用转换为直接引用

(3)初始化 Initialization

执行类的静态代码块(static{})和静态变量显式赋值

2. 创建对象

(1)通过new关键字创建对象

new关键字会直接触发类的完整加载、链接和初始化过程;

① 若类未加载:立即执行加载、链接,完成后强制触发类的初始化(执行static代码块和初始化静态变量)。

② 初始化完成后:调用构造函数创建对象

(2)通过反射创建对象

通过反射(Class.newInstance() 或 Constructor.newInstance() )创建对象时,允许分阶段控制类的加载过程:

① 触发加载但不初始化:使用ClassLoader.loadClass() 可加载类但不初始化:

此时尚未执行静态代码块或静态变量显式赋值。 

② 按需触发初始化:在首次需要初始化时才触发(如反射调用newInstance() ):

③ 选择性初始化控制

通过Class.forName可指定是否初始化:

public class Main {
    public static void main(String[] args) throws Exception {
        // 反射示例:
        ClassLoader loader = MyClass.class.getClassLoader();
        // 加载类但不初始化(第三个参数为类加载器)
        System.out.println("加载类但不初始化1...");
        // 以下第二个参数为“是否初始化”
        Class<?> clazz2 = Class.forName("com.zhaowa.galaxy.reflection.MyClass", false, loader);
        // 加载类但不初始化
        System.out.println("加载类但不初始化2...");
        Class<?> clazz = loader.loadClass("com.zhaowa.galaxy.reflection.MyClass"); // 无输出
        // 触发初始化前,类的静态代码块仍未执行
        System.out.println("准备创建对象...");
        Object obj = clazz.newInstance(); // 输出:静态代码块执行!
        // 加载类同时触发初始化
        System.out.println("加载类同时触发初始化...");
        Class<?> clazz1 = Class.forName("com.zhaowa.galaxy.reflection.MyClass2");
    }
}
class MyClass {
    static {
        System.out.println("静态代码块执行!"); // 初始化触发
    }
}
class MyClass2 {
    static {
        System.out.println("静态代码块2执行!"); // 初始化触发
    }
}

输出结果:

 3. 反射核心用法

反射允许程序在运行时动态获取类的信息并操作类或对象。核心类是Class,关键操作包括

① 动态创建对象(newInstance() )

② 调用方法(method.invoke() )

③ 访问/修改字段(field.get()/set() )

示例:

(1)通过无参构造函数创建实例对象

public class ReflectionExample1 {
    public static void main(String[] args) {
        try {
            // 1. 获取Class对象(触发类加载,可能初始化)
            Class<?> clazz = Class.forName("com.zhaowa.galaxy.reflection.User");

            // 2. 获取无参构造方法(需处理异常)
            Constructor<?> constructor = clazz.getDeclaredConstructor();

            // 3. 调用newInstance()创建实例(无参数)
            Object instance = constructor.newInstance();
            System.out.println("实例创建成功:" + instance.getClass());

        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
class User {
    public User() {
        System.out.println("无参构造函数被调用!");
    }
}

运行结果: 

(2)通过有参构造函数创建实例对象

public class ReflectionExample2 {
    public static void main(String[] args) {
        try {
            // 1. 获取Class对象(注意使用全限定类名)
            Class<?> clazz = Class.forName("com.zhaowa.galaxy.reflection.User2");
            // 2. 指定参数类型列表,获取有参构造方法
            Class<?>[] paramTypes = {String.class, int.class}; // 参数类型顺序严格匹配
            Constructor<?> constructor = clazz.getDeclaredConstructor(paramTypes);
            // 3. 传递参数值实例化对象
            Object[] initArgs = {"张三", 25}; // 参数值顺序与类型列表一致
            Object instance = constructor.newInstance(initArgs);
            System.out.println("实例创建成功:" + instance.getClass());
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
class User2 {
    private String name;
    private int age;
    public User2(String name, int age) {
        this.name = name;
        this.age = age;
        System.out.println("有参构造函数被调用!name=" + name + ", age=" + age);
    }
}

运行结果:

(3)反射通过私有构造函数创建对象,破坏单例模式

class Singleton {
    private static Singleton instance;
    private Singleton() {
        // 私有构造函数
    }
    public static Singleton getInstance() {
        if (instance == null) {
            instance = new Singleton();
        }
        return instance;
    }
}
public class ReflectionExample3 {
    public static void main(String[] args) {
        try {
            // 通过正常方式获取单例对象
            Singleton instance1 = Singleton.getInstance();
            System.out.println("正常实例:" + instance1);
            // 方式 1:通过反射创建新实例(直接访问构造函数)
            Class<Singleton> clazz = Singleton.class;
            Constructor<Singleton> constructor = clazz.getDeclaredConstructor();
            constructor.setAccessible(true); // 访问私有构造函数
            Singleton instance2 = constructor.newInstance();
            // 方式 2:通过反射多次创建实例(动态控制)
            for (int i = 0; i < 3; i++) {
                Constructor<Singleton> ctor = clazz.getDeclaredConstructor();
                ctor.setAccessible(true);
                Singleton instance = ctor.newInstance();
                System.out.println("反射实例 " + (i+1) + ": " + instance);
            }
            // 验证两个实例是否相同
            System.out.println("instance1 == instance2 ? " + (instance1 == instance2));
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

运行结果:

(4)通过反射获得类的public属性值(getField与getDeclaredField两者的区别)

① getField()的特点:

  • 只能获取当前类及继承类中声明为public的属性;
  • 无法获取非public属性;
  • 可以直接访问继承的父类public属性;

② getDeclaredField()的特点:

  • 能获取当前类中声明的所有属性(public、private、protected、默认访问修饰符);
  • 无法获取父类声明的属性;
  • 访问非public属性需要通过setAccessible(true);
class Parent {
    public String parentPublicField = "Parent-Public";
    private String parentPrivateField = "Parent-Private";
}
class Child extends Parent {
    public String childPublicField = "Child-Public";
    private String childPrivateField = "Child-Private";
}
public class ReflectionExample4 {
    public static void main(String[] args) {
        Child child = new Child();
        Class<?> clazz = Child.class;
        try {
            // ======================= 使用 getField() ========================
            // 1. 获取子类的 public 属性(成功)
            Field childPublicField = clazz.getField("childPublicField");
            System.out.println("[getField] 子类 public 属性: " + childPublicField.get(child));
            // 2. 获取父类的 public 属性(成功)
            Field parentPublicField = clazz.getField("parentPublicField");
            System.out.println("[getField] 父类 public 属性: " + parentPublicField.get(child));
            // 3. 尝试获取子类的 private 属性(失败,触发异常)
             clazz.getField("childPrivateField");
        } catch (Exception e) {
            System.err.println("[getField 失败] " + e.getClass().getSimpleName() + ": " + e.getMessage());
        }
        try {
            // ================== 使用 getDeclaredField() ======================
            // 1. 获取子类的 public 属性(成功)
            Field childPublicDeclaredField = clazz.getDeclaredField("childPublicField");
            System.out.println("[getDeclaredField] 子类 public 属性: " + childPublicDeclaredField.get(child));
            // 2. 获取子类的 private 属性(需解除访问限制)
            Field childPrivateDeclaredField = clazz.getDeclaredField("childPrivateField");
            childPrivateDeclaredField.setAccessible(true);  // 强制访问私有属性
            System.out.println("[getDeclaredField] 子类 private 属性: " + childPrivateDeclaredField.get(child));
            // 3. 尝试获取父类的属性(失败,无论是否是 public)
             clazz.getDeclaredField("parentPublicField");
        } catch (Exception e) {
            System.err.println("[getDeclaredField 失败] " + e.getClass().getSimpleName() + ": " + e.getMessage());
        }
    }
}

运行结果:

(5)通过反射获得类的private、protected、默认访问修饰符的属性值

获取对象值时要将对象实例传入,但如果是静态属性值,不用传对象实例,null就可以

class Student {
    private String privateField = "私有属性值";
    protected String protectedField = "受保护属性值";
    String defaultField = "默认访问属性值"; // 包级私有
    public String publicField = "公有属性值";
}
public class ReflectionExample5 {
    public static void main(String[] args) {
        Student student = new Student();
        try {
            // 获取类的 Class 对象
            Class<?> clazz = Student.class;
            // ================== 访问私有属性 ==================
            Field privateField = clazz.getDeclaredField("privateField");
            privateField.setAccessible(true); // 解除私有访问限制
            String privateValue = (String) privateField.get(student);
            System.out.println("privateField: " + privateValue);
            // ================== 访问受保护属性 ==================
            Field protectedField = clazz.getDeclaredField("protectedField");
            protectedField.setAccessible(true); // 无需继承关系即可访问
            String protectedValue = (String) protectedField.get(student);
            System.out.println("protectedField: " + protectedValue);
            // ================== 访问默认(包级私有)属性 ==================
            Field defaultField = clazz.getDeclaredField("defaultField");
            defaultField.setAccessible(true);
            String defaultValue = (String) defaultField.get(student);
            System.out.println("defaultField: " + defaultValue);
            // ================== 访问公有属性 ==================
            // 方式 1:getDeclaredField + setAccessible(强制访问)
            Field publicField1 = clazz.getDeclaredField("publicField");
            publicField1.setAccessible(true); // 即使公有也强制解除限制(非必须)
            String publicValue1 = (String) publicField1.get(student);
            System.out.println("publicField(强制访问): " + publicValue1);
            // 方式 2:直接通过 getField 获取(不推荐,仅用于对比)
            Field publicField2 = clazz.getField("publicField");
            String publicValue2 = (String) publicField2.get(student);
            System.out.println("publicField(正常访问): " + publicValue2);
        } catch (NoSuchFieldException e) {
            System.err.println("字段不存在: " + e.getMessage());
        } catch (IllegalAccessException e) {
            System.err.println("访问权限失败: " + e.getMessage());
        }
    }
}

运行结果: 

(6)通过反射获得类的private方法

class MyClass3 {
    private String privateMethod(String param) {
        return "私有方法被调用,参数: " + param;
    }
}
public class ReflectionExample6 {
    public static void main(String[] args) {
        try {
            // 创建类的实例
            MyClass3 myObject = new MyClass3();
            // 获取Class对象
            Class<?> clazz = MyClass3.class;
            // 获取私有方法,需指定方法名和参数类型
            Method method = clazz.getDeclaredMethod("privateMethod", String.class);
            // 解除访问限制(关键步骤)
            method.setAccessible(true);
            // 调用方法,传入实例及参数
            String result = (String) method.invoke(myObject, "Hello");
            // 输出结果
            System.out.println("调用结果: " + result);
        } catch (NoSuchMethodException e) {
            System.err.println("方法未找到: " + e.getMessage());
        } catch (IllegalAccessException e) {
            System.err.println("非法访问: " + e.getMessage());
        } catch (InvocationTargetException e) {
            System.err.println("方法内部错误: " + e.getCause());
        }
    }
}

运行结果:

 

(7)通过反射实现一个工具BeanUtils,可以将一个对象属性相同的值赋值给另一个对象

class BeanUtils {
    /**
     * 将源对象的属性值复制到目标对象(浅拷贝)
     * @param source 源对象
     * @param target 目标对象
     */
    public static void copyProperties(Object source, Object target) {
        if (source == null || target == null) {
            throw new IllegalArgumentException("源对象和目标对象不能为 null");
        }

        // 获取源对象和目标对象的所有字段(包含父类字段)
        List<Field> sourceFields = getAllFields(source.getClass());
        List<Field> targetFields = getAllFields(target.getClass());

        for (Field sourceField : sourceFields) {
            // 查找目标对象中与源对象字段同名的字段
            Field targetField = findField(targetFields, sourceField.getName());
            if (targetField == null) continue;

            // 检查类型是否兼容(支持基本类型和包装类型)
            if (!isTypeCompatible(sourceField.getType(), targetField.getType())) continue;

            try {
                // 设置字段可访问性
                sourceField.setAccessible(true);
                targetField.setAccessible(true);

                // 复制值
                Object value = sourceField.get(source);
                targetField.set(target, value);
            } catch (IllegalAccessException e) {
                // 处理无法访问的异常
                System.err.println("字段复制失败: " + sourceField.getName() + " -> " + targetField.getName());
            }
        }
    }

    /**
     * 获取类及其父类的所有字段(非静态)
     */
    private static List<Field> getAllFields(Class<?> clazz) {
        List<Field> fields = new java.util.ArrayList<>();
        while (clazz != null && clazz != Object.class) {
            fields.addAll(Arrays.stream(clazz.getDeclaredFields())
                    .filter(f -> !isStatic(f))
                    .collect(Collectors.toList()));
            clazz = clazz.getSuperclass();
        }
        return fields;
    }

    /**
     * 根据字段名从字段列表中查找字段
     */
    private static Field findField(List<Field> fields, String fieldName) {
        return fields.stream()
                .filter(f -> f.getName().equals(fieldName))
                .findFirst()
                .orElse(null);
    }

    /**
     * 判断字段是否为静态
     */
    private static boolean isStatic(Field field) {
        return java.lang.reflect.Modifier.isStatic(field.getModifiers());
    }

    /**
     * 判断源类型与目标类型是否兼容
     */
    private static boolean isTypeCompatible(Class<?> sourceType, Class<?> targetType) {
        // 处理基本类型与包装类型的兼容(例如 int -> Integer)
        if (sourceType.isPrimitive()) {
            return targetType.isPrimitive() ? sourceType.equals(targetType) : getWrapperType(sourceType).equals(targetType);
        } else if (targetType.isPrimitive()) {
            return getWrapperType(targetType).equals(sourceType);
        } else {
            return targetType.isAssignableFrom(sourceType);
        }
    }

    /**
     * 获取基本类型对应的包装类型
     */
    private static Class<?> getWrapperType(Class<?> primitiveType) {
        if (primitiveType == int.class) return Integer.class;
        if (primitiveType == long.class) return Long.class;
        if (primitiveType == boolean.class) return Boolean.class;
        if (primitiveType == byte.class) return Byte.class;
        if (primitiveType == char.class) return Character.class;
        if (primitiveType == short.class) return Short.class;
        if (primitiveType == double.class) return Double.class;
        if (primitiveType == float.class) return Float.class;
        return primitiveType;
    }
}

// 父类
class Person {
    protected String name;
    private int age;
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public int getAge() {
        return age;
    }
    public void setAge(int age) {
        this.age = age;
    }
}

// 子类
class Employee extends Person {
    private double salary;
    private Integer departmentId;

    public double getSalary() {
        return salary;
    }

    public void setSalary(double salary) {
        this.salary = salary;
    }

    public Integer getDepartmentId() {
        return departmentId;
    }

    public void setDepartmentId(Integer departmentId) {
        this.departmentId = departmentId;
    }
}

// 目标类
class EmployeeDTO {
    private String name;
    private int age;        // 基本类型
    private Double salary;  // 包装类型
    private Integer departmentId;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public Double getSalary() {
        return salary;
    }

    public void setSalary(Double salary) {
        this.salary = salary;
    }

    public Integer getDepartmentId() {
        return departmentId;
    }

    public void setDepartmentId(Integer departmentId) {
        this.departmentId = departmentId;
    }
}

public class ReflectionExample7 {
    public static void main(String[] args) {
        // 准备数据
        Employee emp = new Employee();
        emp.name = "张三";
        emp.setAge(30);
        emp.setSalary(15000.5);
        emp.setDepartmentId(101);
        // 目标对象
        EmployeeDTO dto = new EmployeeDTO();
        // 复制属性
        BeanUtils.copyProperties(emp, dto);
        // 验证结果
        System.out.println("DTO.name: " + dto.getName());
        System.out.println("DTO.age: " + dto.getAge());
        System.out.println("DTO.salary: " + dto.getSalary());
        System.out.println("DTO.departmentId: " + dto.getDepartmentId());
    }
}

运行结果:

二、泛型

泛型是编程语言中一种支持参数化类型的特性,允许在定义类、接口、方法时使用类型参数(TypeParameters),而在使用时执行具体的类型。泛型的核心目标是提高代码的类型安全性可重用性可读性

参数化类型

定义时不确定类型:在编写类、接口或方法时使用类型占位符(如<T>),实际使用时再传入具体类型(如String、Integer)

类型安全和编译时检查

编译器可以检查参数类型是否正确,并在编译时而不是运行时捕获类型错误。

1. 泛型的重要性

(1)解决类型安全问题

在泛型出现之前,Java集合类(如ArrayList、HashMap)默认使用Object类型存储元素。这导致两个问题:

① 强制类型转换:从集合中取出元素时需要显示转换类型;

② 运行时类型错误风险:如果类型不一致,会抛出ClassCastException。

示例:没有泛型的代码:

通过泛型改进后:

(2)避免重复代码

在没有泛型时,如果要支持不同类型,需要为每种类型编写相似的代码(如分别实现IntegerBox、StringBox)。

泛型的代码复用:

(3)提高可读性和维护性

泛型让代码的意图更明确,例如Map<String, Integer>清楚地表示“键是String,值是Integer”。

清晰的类型约束:直接声明数据类型,避免混乱的类型转换。

自解释性:代码能直接表达其设计的通用性。

2. 泛型用法

(1)泛型类

将泛型定义在类上,用户使用该类的时候,才把类型明确下来。这样的话,用户明确了什么类型,该类就代表着什么类型,用户在使用的时候就不用担心强转的问题和运行时转换异常的问题了。

(2)泛型方法

除了在类上使用泛型,可能就仅仅在某个方法上需要使用泛型,外界仅仅关心该方法,不关心类其他的属性,这时可以采用类型方法。

(3)泛型类派生子类

泛型类是拥有泛型特性的类,本质上还是一个Java类,所以可以被继承或实现。

class Box<T> {
    private T content;  // 泛型字段

    public void setContent(T content) {
        System.out.println("调用父类 setContent 方法");
        this.content = content;
    }

    public T getContent() {
        System.out.println("调用父类 getContent 方法");
        return content;
    }
}

① 子类固定父类泛型类型(StringBox 继承自 Box<String>)

子类直接指定父类泛型参数的具体类型,使用于特定场景的扩展

// 子类继承时指定父类泛型类型为 String
class StringBox extends Box<String> {
    // 新增方法:专为字符串内容设计
    public void toUpperCase() {
        String value = getContent(); // 直接使用 String 类型
        if (value != null) {
            setContent(value.toUpperCase());
        }
    }
    @Override
    public void setContent(String value) { // 重写父类方法,参数类型为 String
        super.setContent(value.toUpperCase());
    }

    @Override
    public String getContent() { // 重写父类方法,返回类型为 String
        return super.getContent();
    }

}
public class Main {
    public static void main(String[] args) {
        StringBox box = new StringBox();
        box.setContent("hello");
        box.toUpperCase();
        System.out.println(box.getContent()); // 输出: HELLO
    }
}

运行结果:

 

② 子类保留父类泛型类型(AdvancedBox<T> 继承自 Box<T>)

子类保持父类泛型参数的灵活性,并扩展新功能

// 父类定义不变,与示例1相同

// 子类保留父类的泛型参数 <T>
class AdvancedBox<T> extends Box<T> {
    // 新增方法:检查内容是否为空
    public boolean isEmpty() {
        return getContent() == null;
    }

    // 新增方法:重置内容为 null
    public void clear() {
        setContent(null);
    }
}

public class Main2 {
    public static void main(String[] args) {
        AdvancedBox<Integer> intBox = new AdvancedBox<>();
        intBox.setContent(100);
        System.out.println(intBox.isEmpty()); // 输出: false
        intBox.clear();
        System.out.println(intBox.getContent()); // 输出: null
    }
}

运行结果:

 

③ 添加子类自己的泛型参数(KeyValuePair<K, V> 继承自 Pair<K>)

父子类均使用泛型,子类引入新的类型参数

// 泛型父类
class Pair<T> {
    private T first;
    private T second;

    public Pair(T first, T second) {
        this.first = first;
        this.second = second;
    }

    public T getFirst() { return first; }
    public T getSecond() { return second; }
}

// 子类添加新的泛型参数 V,与父类参数 K 独立
class KeyValuePair<K, V> extends Pair<K> {
    private V value;

    public KeyValuePair(K key, V value) {
        super(key, key); // 父类需要两个同类型参数,此处复用 key 作为示例
        this.value = value;
    }

    // 新增方法:获取键值对的独立值
    public V getValue() { return value; }
}

public class Main3 {
    public static void main(String[] args) {
        KeyValuePair<String, Integer> entry = new KeyValuePair<>("Age", 30);
        String key = entry.getFirst(); // 类型为 String(父类返回值)
        int value = entry.getValue();  // 类型为 Integer(子类新增方法)
        System.out.println(key + ": " + value); // 输出: Age: 30
    }
}

运行结果:

 

④ 约束父类类型边界(InCalculator 继承自 NumberCalculator<Integer>)

子类继承时遵循父类的泛型约束(如<T extends Number>)并进一步具体化

// 泛型父类,约束类型必须为 Number 的子类
class NumberCalculator<T extends Number> {
    protected T value;

    public NumberCalculator(T value) {
        this.value = value;
    }

    public double doubleValue() {
        return value.doubleValue();
    }
}

// 子类指定父类泛型类型为 Integer
class IntCalculator extends NumberCalculator<Integer> {
    public IntCalculator(Integer value) {
        super(value);
    }

    // 新增方法:计算平方(专为 Integer 设计)
    public int squareInt() {
        return value * value;
    }
}

public class Main4 {
    public static void main(String[] args) {
        IntCalculator calc = new IntCalculator(5);
        System.out.println(calc.doubleValue()); // 输出: 5.0(调用父类方法)
        System.out.println(calc.squareInt());   // 输出: 25(子类专属方法)
    }
}

运行结果:

(4)类型通配符

① 无界通配符(<?>)

用于处理未知类型的集合,适合只读取集合内容的场景

public class UnboundedWildcardDemo {
    /**
     * 打印任意类型集合的元素
     * @param list 使用无界通配符 <?>,表示接受任意类型的 List
     */
    public static void printList(List<?> list) {
        for (Object item : list) {
            System.out.print(item + " ");
        }
        System.out.println();
    }

    public static void main(String[] args) {
        List<String> names = Arrays.asList("Alice", "Bob", "Charlie");
        List<Integer> numbers = Arrays.asList(1, 2, 3);

        printList(names);   // 输出: Alice Bob Charlie
        printList(numbers); // 输出: 1 2 3
    }
}

② 上界通配符(<? extends Number>)

限制类型为Number 或其子类(如Integer、Double),适合读取子类

public class UpperBoundedWildcardDemo {
    /**
     * 计算数值列表的总和
     * @param list 使用上界通配符 <? extends Number>
     * @return Sum of numbers as double
     */
    public static double sumOfList(List<? extends Number> list) {
        double sum = 0.0;
        for (Number num : list) {
            sum += num.doubleValue(); // 调用 Number 的方法
        }
        return sum;
    }

    public static void main(String[] args) {
        List<Integer> integers = Arrays.asList(1, 2, 3);
        List<Double> doubles = Arrays.asList(1.1, 2.2, 3.3);

        System.out.println(sumOfList(integers)); // 输出: 6.0
        System.out.println(sumOfList(doubles));  // 输出: 6.6
    }
}

③ 下界通配符(<? super Integer>)

限制类型为Integer或其父类(如Number、Object),适合写入场景:

public class LowerBoundedWildcardDemo {

    /**
     * 向集合中添加多个 Integer 值
     * @param dest 使用下界通配符 <? super Integer>
     * @param values 要添加的多个 Integer 值
     */
    public static void addNumbersToList(List<? super Integer> dest, List<Integer> values) {
        dest.addAll(values); // 可安全写入 Integer 或其父类型(如 Number、Object)
    }

    public static void main(String[] args) {
        List<Number> numberList = new ArrayList<>();
        addNumbersToList(numberList, Arrays.asList(10, 20, 30));
        System.out.println(numberList); // 输出: [10, 20, 30]

        List<Object> objectList = new ArrayList<>();
        addNumbersToList(objectList, Arrays.asList(100, 200));
        System.out.println(objectList); // 输出: [100, 200]
    }
}

3. 泛型擦除

(1)为什么需要泛型擦除

泛型擦除是Java为向后兼容性设计的关键机制,具体原因如下:

① 兼容旧版本代码

java在5.0版本(2004年)引入泛型。

泛型擦除让新代码(带泛型)和旧代码(无泛型)共享同一个类文件结构。例如:

泛型擦除后,两者本质都是List,从而保证旧代码无需修改即可在新JVM上运行。 

② 避免JVM修改

泛型擦除无需修改JVM的底层机制。JVM仍使用同样的字节码指令处理泛型和非泛型集合,极大降低了实现复杂度。

③ 减少代码冗余

如果泛型类行不擦除,List<String> 和 List<Integer> 会分别生成不同的类文件,导致代码膨胀;擦除后,所有泛型实例共享同一个类文件,提升执行效率。

(2)泛型擦除带来的限制

① 无法使用基本类型参数

② 运行时无法获取泛型类型信息

③ 无法创建泛型数组

4. 桥接方法

(1)问题

① 由于泛型擦除,Box<String>编译后的原始方法是:

set(Object value) --> 原来set(String value)

get() 的返回值类型是Object(原来String)

② StringBox中的重写方法是set(String) 和 get(): String,与父类擦除后的set(Object) 和 get(): Object方法签名不匹配。

这会导致多态性失效:通过Box<String> box = new StringBox() 调用set方法时,JVM期望的签名是set(Object),但StringBox只有set(String)。

(2)桥接方法的作用

① 保证方法重写的多态性

  • JVM通过方法签名(方法名+参数类型)和返回类型确定方法分派。
  • 在类型擦除后,父类与子类的方法签名不匹配,桥接方法充当中间层,确保父类引用能正确调用子类的具体方法。

② 强制类型安全

桥接方法中会插入类型检查:

若传入非法类型(如Integer),运行时会抛出ClassCastException,避免了类型混乱。

// 子类继承时指定父类泛型类型为 String
class StringBox extends Box<String> {
    // 新增方法:专为字符串内容设计
    public void toUpperCase() {
        String value = getContent(); // 直接使用 String 类型
        if (value != null) {
            setContent(value.toUpperCase());
        }
    }
    @Override
    public void setContent(String value) { // 重写父类方法,参数类型为 String
        super.setContent(value.toUpperCase());
    }

    @Override
    public String getContent() { // 重写父类方法,返回类型为 String
        return super.getContent();
    }

}
public class BridgeMethodDemo {
    public static void main(String[] args) {
        for (Method method : StringBox.class.getDeclaredMethods()) {
            if (method.isBridge()) {
                System.out.println("桥接方法:" + method);
            }
        }
    }
}

运行结果为:

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2328536.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

元宇宙浪潮下,前端开发如何“乘风破浪”?

一、元宇宙对前端开发的新要求 元宇宙的兴起&#xff0c;为前端开发领域带来了全新的挑战与机遇。元宇宙作为一个高度集成、多维互动的虚拟世界&#xff0c;要求前端开发不仅具备传统网页开发的能力&#xff0c;还需要掌握虚拟现实&#xff08;VR&#xff09;、增强现实&#…

2025年3月 Scratch 图形化(二级)真题解析 中国电子学会全国青少年软件编程等级考试

2025.03Scratch图形化编程等级考试二级真题试卷 一、选择题 第 1 题 甲、乙、丙、丁、戊五人参加100米跑比赛&#xff0c;甲说:“我的前面至少有两人&#xff0c;但我比丁快。”乙说:“我的前面是戊。”丙说:“我的后面还有两个人。”请从前往后&#xff08;按照速度快慢&a…

从代码学习深度学习 - GRU PyTorch版

文章目录 前言一、GRU模型介绍1.1 GRU的核心机制1.2 GRU的优势1.3 PyTorch中的实现二、数据加载与预处理2.1 代码实现2.2 解析三、GRU模型定义3.1 代码实现3.2 实例化3.3 解析四、训练与预测4.1 代码实现(utils_for_train.py)4.2 在GRU.ipynb中的使用4.3 输出与可视化4.4 解析…

二叉树 递归

本篇基于b站灵茶山艾府的课上例题与课后作业。 104. 二叉树的最大深度 给定一个二叉树 root &#xff0c;返回其最大深度。 二叉树的 最大深度 是指从根节点到最远叶子节点的最长路径上的节点数。 示例 1&#xff1a; 输入&#xff1a;root [3,9,20,null,null,15,7] 输出&…

反常积分和定积分的应用 2

世界尚有同类 前言伽马函数的推论关于数学的思考平面图形的面积笛卡尔心形线伯努利双纽线回顾参数方程求面积星型线摆线 旋转体体积一般轴线旋转被积函数有负数部分曲线的弧长最后一个部分内容-旋转曲面侧表面积直角坐标系极坐标系参数方程 总结 前言 力大出奇迹。好好加油。 …

Element-plus弹出框popover,使用自定义的图标选择组件

自定义的图标选择组件是若依的项目的 1. 若依的图标选择组件 js文件&#xff0c;引入所有的svg图片 let icons [] // 注意这里的路径&#xff0c;一定要是自己svg图片的路径 const modules import.meta.glob(./../../assets/icons/svg/*.svg); for (const path in modules)…

思维链 Chain-of-Thought(COT)

思维链 Chain-of-Thought&#xff08;COT&#xff09;&#xff1a;思维链的启蒙 3. 思维链 Chain-of-Thought&#xff08;COT&#xff09;存在问题&#xff1f;2. 思维链 Chain-of-Thought&#xff08;COT&#xff09;是思路是什么&#xff1f;1. 什么是 思维链 Chain-of-Thoug…

硬件电路(23)-输入隔离高低电平有效切换电路

一、概述 项目中为了防止信号干扰需要加一些隔离电路&#xff0c;而且有时传感器的信号是高有效有时是低有效&#xff0c;所以基于此背景&#xff0c;设计了一款方便实现高低电平有效检测切换电路。 二、应用电路

大模型学习二:DeepSeek R1+蒸馏模型组本地部署与调用

一、说明 DeepSeek R1蒸馏模型组是基于DeepSeek-R1模型体系&#xff0c;通过知识蒸馏技术优化形成的系列模型&#xff0c;旨在平衡性能与效率。 1、技术路径与核心能力 基础架构与训练方法‌ ‌DeepSeek-R1-Zero‌&#xff1a;通过强化学习&#xff08;RL&#xff09;训练&…

相机的曝光和增益

文章目录 曝光增益增益原理主要作用增益带来的影响增益设置与应用 曝光 参考&#xff1a;B站优致谱视觉 增益 相机增益是指相机在拍摄过程中对图像信号进行放大的一种操作&#xff0c;它在提高图像亮度和增强图像细节方面起着重要作用&#xff0c;以下从原理、作用、影响以…

Linux内核物理内存组织结构

一、系统调用sys_mmap 系统调用mmap用来创建内存映射&#xff0c;把创建内存映射主要的工作委托给do_mmap函数&#xff0c;内核源码文件处理&#xff1a;mm/mmap.c 二、系统调用sys_munmap 1、vma find_vma (mm, start); // 根据起始地址找到要删除的第一个虚拟内存区域 vma 2…

(多看) CExercise_05_1函数_1.2计算base的exponent次幂

题目&#xff1a; 键盘录入两个整数&#xff1a;底(base)和幂指数(exponent)&#xff0c;计算base的exponent次幂&#xff0c;并打印输出对应的结果。&#xff08;注意底和幂指数都可能是负数&#xff09; 提示&#xff1a;求幂运算时&#xff0c;基础的思路就是先无脑把指数转…

Vuue2 element-admin管理后台,Crud.js封装表格参数修改

需求 表格数据调用列表接口&#xff0c;需要多传一个 Type字段&#xff0c;而Type字段的值 需要从跳转页面Url上面获取到&#xff0c;并赋值给Type&#xff0c;再传入列表接口中&#xff0c;最后拿到表格数据并展示 遇到的问题 需求很简单&#xff0c;但是因为表格使用的是统…

Tiktok矩阵运营中使用云手机的好处

Tiktok矩阵运营中使用云手机的好处 云手机在TikTok矩阵运营中能够大幅提高管理效率、降低封号风险&#xff0c;并节省成本&#xff0c;是非常实用的运营工具。TikTok矩阵运营使用云手机有很多优势&#xff0c;特别是对于需要批量管理账号、提高运营效率的团队来说。以下是几个…

Linux下调试器gdb_cgdb使用

文章目录 一、样例代码二、使用watchset var确定问题原因条件断点 一、样例代码 #include <stdio.h>int Sum(int s, int e) {int result 0;int i;for(i s; i < e; i){result i;}return result; }int main() {int start 1;int end 100;printf("I will begin…

Vite环境下解决跨域问题

在 Vite 开发环境中&#xff0c;可以通过配置代理来解决跨域问题。以下是具体步骤&#xff1a; 在项目根目录下找到 vite.config.js 文件&#xff1a;如果没有&#xff0c;则需要创建一个。配置代理&#xff1a;在 vite.config.js 文件中&#xff0c;使用 server.proxy 选项来…

超简单:Linux下opencv-gpu配置

1.下载opencv和opencv_contrib安装包 1&#xff09;使用命令下 git clone https://github.com/opencv/opencv.git -b 4.9.0 git clone https://github.com/opencv/opencv_contrib.git -b 4.9.02&#xff09;复制链接去GitHub下载然后上传到服务器 注意&#xff1a;看好版本&a…

泰博云平台solr接口存在SSRF漏洞

免责声明&#xff1a;本号提供的网络安全信息仅供参考&#xff0c;不构成专业建议。作者不对任何由于使用本文信息而导致的直接或间接损害承担责任。如涉及侵权&#xff0c;请及时与我联系&#xff0c;我将尽快处理并删除相关内容。 漏洞描述 SSRF漏洞是一种在未能获取服务器…

31天Python入门——第20天:魔法方法详解

你好&#xff0c;我是安然无虞。 文章目录 魔法方法1. __new__和__del__2. __repr__和__len__3. __enter__和__exit__4. 可迭代对象和迭代器5. 中括号[]数据操作6. __getattr__、__setattr__ 和 __delattr__7. 可调用的8. 运算符 魔法方法 魔法方法: Python中的魔法方法是一类…

ubantu22.04中搭建地图开发环境(qt5.15.2 + osg3.7.0 + osgearth3.7.1 + osgqt)

一、下载安装qt5.15.2 二、下载编译安装osg3.7.0 三、下载编译安装osgearth3.7.1 四、下载编译安装osgqt 五、二三维地图显示demo开发 六、成果展示&#xff1a; 已有功能&#xff1a;加载了dom影像、可以进行二三维地图切换显示、二维地图支持缩放和平移、三维地图支持旋转…