泛型_19

泛型

泛型(Generics) 是 Java 5 引入的一项重要特性,它允许在定义类、接口和方法时使用类型参数,从而提高代码的复用性、类型安全性和可读性。泛型的核心思想是 参数化类型,即在使用时才指定具体的类型。

泛型的作用

  • 类型安全:在编译时检查类型,避免运行时的类型转换错误
  • 代码复用:通过泛型可以编写通用的代码,适用于多种类型。
  • 可读性:泛型使代码更加清晰,减少了强制类型转换的需要

泛型的使用范围

  • 泛型类:在类定义时使用类型参数。
  • 泛型接口:在接口定义时使用类型参数。
  • 泛型方法:在方法定义时使用类型参数

泛型类

  • 泛型类是在类定义时使用类型参数的类。类型参数用尖括号 < > 表示,通常用单个大写字母(如 T、E、K、V 等)表示

    // 定义一个泛型类
    class Box<T> {
        private T value;
    
        public void setValue(T value) {
            this.value = value;
        }
    
        public T getValue() {
            return value;
        }
    }
    
    public class GenericClassExample {
        public static void main(String[] args) {
            // 使用泛型类,指定类型为 Integer
            Box<Integer> integerBox = new Box<>();
            integerBox.setValue(123);
            System.out.println("Integer Value: " + integerBox.getValue());
    
            // 使用泛型类,指定类型为 String
            Box<String> stringBox = new Box<>();
            stringBox.setValue("Hello");
            System.out.println("String Value: " + stringBox.getValue());
        }
    }
    

泛型接口

  • 泛型接口是在接口定义时使用类型参数的接口

    // 定义一个泛型接口
    interface Pair<K, V> {
        K getKey();
        V getValue();
    }
    
    // 实现泛型接口
    class OrderedPair<K, V> implements Pair<K, V> {
        private K key;
        private V value;
    
        public OrderedPair(K key, V value) {
            this.key = key;
            this.value = value;
        }
    
        @Override
        public K getKey() {
            return key;
        }
    
        @Override
        public V getValue() {
            return value;
        }
    }
    

泛型方法

  • 泛型方法是在方法定义时使用类型参数的方法。类型参数用尖括号 < > 表示,放在方法的返回类型之前

    class Utils {
        // 定义一个泛型方法
        public static <T> void printArray(T[] array) {
            for (T element : array) {
                System.out.println(element);
            }
        }
    }
    
    public class GenericMethodExample {
        public static void main(String[] args) {
            // 使用泛型方法
            Integer[] intArray = {1, 2, 3};
            String[] strArray = {"A", "B", "C"};
    
            Utils.printArray(intArray); // 输出: 1 2 3
            Utils.printArray(strArray); // 输出: A B C
        }
    }
    

泛型的类型擦除

  • Java 的泛型是通过 类型擦除(Type Erasure) 实现的。在编译时,泛型类型参数会被擦除,替换为它们的上限(通常是 Object)

  • 类型擦除是 Java 实现泛型的一种方式。泛型的引入主要是为了在编译阶段提供更严格的类型检查,同时允许代码的复用。然而,Java 为了兼容之前没有泛型的代码,采用了类型擦除机制。简单来说,类型擦除就是在编译过程中,将泛型类型信息从代码中移除,把泛型类型参数替换为具体的类型,这个具体类型通常是它们的上限(如果没有指定上限,默认上限是 Object),从而生成普通的、非泛型的字节码

  • 当定义泛型类、接口或方法时,如果泛型类型参数没有指定边界,在编译时这些类型参数会被擦除为 Object 类型

  • 如果泛型类型参数指定了边界,在编译时会被擦除为其边界类型

    // 编译前的泛型类
    class Box<T> {
        private T value;
        public void setValue(T value) { this.value = value; }
        public T getValue() { return value; }
    }
    
    // 编译后的类(类型擦除后)
    class Box {
        private Object value;
        public void setValue(Object value) { this.value = value; }
        public Object getValue() { return value; }
    }
    

泛型的通配符

  1. 无界通配符(<?>):表示任意类型

    public void printList(List<?> list) {
        for (Object element : list) {
            System.out.println(element);
        }
    }
    
  2. 上界通配符(<? extends T>):表示类型是 T 或其子类

    public void printNumbers(List<? extends Number> list) {
        for (Number number : list) {
            System.out.println(number);
        }
    }
    
  3. 下界通配符(<? super T>):表示类型是 T 或其父类

    public void addNumbers(List<? super Integer> list) {
        list.add(123);
    }
    

    extends 可以用于泛型类型参数的声明,用于约束类型参数的上限。

    使用 extends 声明泛型类型参数上限

    class GenericExample { 
        // 使用 extends 声明泛型类型参数 T 必须是 Number 或其子类 
        <T extends Number> void genericMethod(T param) { 
            System.out.println("Generic  method with number parameter: " + param); 
        } 
     
        public static void main(String[] args) { 
            GenericExample example = new GenericExample(); 
            // 可以传入 Integer 类型,因为 Integer 是 Number 的子类 
            example.genericMethod(10);  
            // 可以传入 Double 类型,因为 Double 是 Number 的子类 
            example.genericMethod(3.14);  
        } 
    } 
    //在上述代码中, <T extends Number> 表示泛型类型参数 T 必须是 Number 或者 Number 的子类,这样就可以在方法中安全地处理 Number 类型及其子类的对象
    

    super 不能直接用于泛型类型参数的声明,它主要用于泛型通配符的场景

    super 表示类型参数的下界,编译器无法确定类型参数的具体范围(如 Number 或其父类),因此不能用于泛型类型参数

    import java.util.ArrayList;  
    import java.util.List;  
     
    class GenericExample { 
        // 使用 super 作为通配符,list 的元素类型必须是 Number 或其父类 
        static void addNumbers(List<? super Number> list) { 
            list.add(10);  
            list.add(3.14);  
        } 
     
        public static void main(String[] args) { 
            List<Object> objectList = new ArrayList<>(); 
            // 可以传入 List<Object>,因为 Object 是 Number 的父类 
            addNumbers(objectList); 
            System.out.println(objectList);  
        } 
    } 
    //在上述代码中,List<? super Number> 表示这个 List 的元素类型必须是 Number 或者 Number 的父类,这样在 addNumbers 方法中就可以安全地向 List 中添加 Number 类型的对象
    

泛型的继承关系

泛型类或接口可以像普通类或接口一样被继承或实现。子类或实现类可以选择保留泛型类型参数,也可以指定具体的类型

  • 保留泛型类型参数

    // 泛型父类
    class Box<T> {
        private T value;
    
        public void setValue(T value) {
            this.value = value;
        }
    
        public T getValue() {
            return value;
        }
    }
    
    // 子类保留泛型类型参数
    class StringBox<T> extends Box<T> {
        // 可以添加子类特有的方法
    }
    
    //实现
    public class GenericInheritanceExample {
        public static void main(String[] args) {
            StringBox<String> box = new StringBox<>();
            box.setValue("Hello");
            System.out.println(box.getValue()); // 输出: Hello
        }
    }
    
  • 指定具体类型

    // 子类指定具体类型
    class IntegerBox extends Box<Integer> {
        // 可以添加子类特有的方法
    }
    
    public class GenericInheritanceExample {
        public static void main(String[] args) {
            IntegerBox box = new IntegerBox();
            box.setValue(123);
            System.out.println(box.getValue()); // 输出: 123
        }
    }
    

泛型方法也可以被继承。子类可以重写父类的泛型方法,但必须保持方法签名一致

虽然泛型的名称(如 中的 T)不是方法签名的一部分,但泛型类型参数的边界(如果有的话)必须保持一致

class Parent {
    // 泛型方法
    public <T> void print(T value) {
        System.out.println("Parent: " + value);
    }
}

class Child extends Parent {
    // 重写泛型方法  其中代表泛型的字母可以不同
    @Override
    public <T> void print(T value) {
        System.out.println("Child: " + value);
    }
}

public class GenericMethodInheritanceExample {
    public static void main(String[] args) {
        Parent parent = new Parent();
        parent.print("Hello"); // 输出: Parent: Hello

        Child child = new Child();
        child.print("Hello"); // 输出: Child: Hello
    }
}
----------------------------------------------
class Parent { 
    <T extends Number> void genericMethod(T param) { 
        System.out.println("Parent's  generic method"); 
    } 
} 
 
class Child extends Parent { 
    // 泛型类型参数的边界保持一致 
    @Override 
    <T extends Number> void genericMethod(T param) { 
        System.out.println("Child's  generic method"); 
    } 
} 

泛型注意事项

  • 泛型类型参数在运行时是不可见的

  • 不能直接创建泛型类型的实例(如 new T())

  • 不能直接创建泛型数组(如 new T[10])

  • 不能使用基本类型:泛型类型参数必须是引用类型(如 Integer 而不是 int)

  • 在指定泛型具体类型后,可以传入该类型或者其子类型

  • 泛型类型的继承关系与普通类型的继承关系不同。即使两个类的类型参数有继承关系,它们的泛型类型之间也没有继承关系

  • Box 不是 Box

posted @   QAQ001  阅读(7)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· Manus的开源复刻OpenManus初探
· AI 智能体引爆开源社区「GitHub 热点速览」
· C#/.NET/.NET Core技术前沿周刊 | 第 29 期(2025年3.1-3.9)
· 从HTTP原因短语缺失研究HTTP/2和HTTP/3的设计差异
点击右上角即可分享
微信分享提示