Java进阶之泛型

泛型 (Generics)
定义
  • 泛型:允许在定义类、接口和方法时使用类型参数,从而在编译时捕获类型错误,提高代码的类型安全性和复用性。
主要用途
  1. 类型安全:避免类型转换错误,编译时检查类型安全。
  2. 代码复用:通过泛型可以编写通用的类和方法,适用于多种数据类型。
  3. 消除强制类型转换:在使用泛型时,编译器会自动进行类型转换,减少代码中的强制类型转换。
泛型的基本语法
标记符
复制代码
T 是类型参数,通常使用 T、E、K、V 等字母来表示不同类型。
使用时,必须指定 T 的具体类型,例如:Box<String> box = new Box<>();
E - Element (在集合中使用,因为集合中存放的是元素)
T - Type(Java 类)
K - Key(键)
V - Value(值)
N - Number(数值类型)
    
/*注意标记符只是起一个提示作用并不代表你写了E就只能传入集合类型,这些字母其实可以自己定义。
  所有字母的作用都是一样的代表不确定类型*/
 //比如
    public static void main(String[] args) {
        add(1);
        add("2");
        add(new int[]{1, 2, 3});
    }

    public static <A> void add(A a) {
        System.out.println(a);
    }
泛型类
复制代码
public class Box<T> {
    private T item;

    public void set(T item) {
        this.item = item;
    }

    public T get() {
        return item;
    }
}

public class Main {
    public static void main(String[] args) {
        Box<Integer> integerBox = new Box<>();
        integerBox.set(10);
        System.out.println(integerBox.get()); // 输出: 10

        Box<String> stringBox = new Box<>();
        stringBox.set("Hello");
        System.out.println(stringBox.get()); // 输出: Hello
    }
}
泛型接口
复制代码
public interface Container<T> {
    void add(T item);
    T get(int index);
}

public class ArrayListContainer<T> implements Container<T> {
    private List<T> list = new ArrayList<>();

    @Override
    public void add(T item) {
        list.add(item);
    }

    @Override
    public T get(int index) {
        return list.get(index);
    }
}

public class Main {
    public static void main(String[] args) {
        Container<String> container = new ArrayListContainer<>();
        container.add("Hello");
        container.add("World");
        System.out.println(container.get(0)); // 输出: Hello
        System.out.println(container.get(1)); // 输出: World
    }
}
泛型方法
复制代码
public class Util {
    public static <T> void printArray(T[] array) {
        for (T item : array) {
            System.out.println(item);
        }
    }
}

public class Main {
    public static void main(String[] args) {
        Integer[] intArray = {1, 2, 3};
        Util.printArray(intArray); // 输出: 1, 2, 3

        String[] stringArray = {"Hello", "World"};
        Util.printArray(stringArray); // 输出: Hello, World
    }
}
泛型的边界
上界(extends)
  • 定义:表示类型参数是某个特定类型的子类型。

  • 用途:用于限制泛型参数的类型范围,确保类型安全。

    public class Test {

    //限制T的类型必须是继承自Number类型的,所以我们传入的类型只能是Number的子类,

    //及java中值类型所对应的引用类型 int->Integer

    public static void printList(List list) {

    for (Number num : list) {

    System.out.println(num);

    }

    }

    复制代码
    public static void main(String[] args) {
        List<Integer> intList = Arrays.asList(1, 2, 3);
        printList(intList); // 输出: 1, 2, 3
    
        List<Double> doubleList = Arrays.asList(1.1, 2.2, 3.3);
        printList(doubleList); // 输出: 1.1, 2.2, 3.3
    }

    }

下界 (super)
  • 定义:表示类型参数是某个特定类型的父类型。
  • 用途:用于限制泛型参数的类型范围,确保类型安全。

错误写法

复制代码
  public static <T super Integer> void printList(List<T> list) {
        for (Number num : list) {
            System.out.println(num);
        }
    }

    public static void main(String[] args) {
        List<Integer> intList = Arrays.asList(1, 2, 3);
        printList(intList); // 输出: 1, 2, 3

        List<Double> doubleList = Arrays.asList(1.1, 2.2, 3.3);
        printList(doubleList); // 输出: 1.1, 2.2, 3.3
    }
/*
在Java中,泛型的类型参数不能使用super关键字来定义下界。正确的语法是使用extends关键字来定义上界,或者在方法参数中使用通配符加上super关键字来定义下界。
我们的代码尝试定义了一个泛型方法addNumbers,该方法接受一个列表参数,列表中的元素类型为T,并且T应该是Integer的超类型(即可以是Integer本身或它的父类)
但是,<T super Integer>这种写法是不正确的。如果你想表达这个意思,应该使用通配符? super Integer来代替。
*/

对于下界(lower bound)的定义,Java 泛型只支持使用通配符 ? super 来表示。具体来说,? super T 表示类型 T 及其所有父类型。

通配符()

表示未知类型,适用于参数未知的通用集合。

复制代码
java复制代码public static void printList(List<?> list) {
    for (Object obj : list) {
        System.out.println(obj);
    }
}

所以下界的正确写法为

复制代码
public static void addNumbers(List<? super Integer> list) {
    list.add(1);
    list.add(2);
    list.add(3);
}

public static void main(String[] args) {
    List<Integer> intList = new ArrayList<>();
    addNumbers(intList); // 向intList中添加1, 2, 3

    List<Number> numberList = new ArrayList<>();
    addNumbers(numberList); // 向numberList中添加1, 2, 3

    List<Object> objectList = new ArrayList<>();
    addNumbers(objectList); // 向objectList中添加1, 2, 3
}

上界通配符写法

复制代码
   public static void printList(List<? extends Number> list) {
        for (Number num : list) {
            System.out.println(num);
        }
    }

    public static void main(String[] args) {
        List<Integer> intList = Arrays.asList(1, 2, 3);
        printList(intList); // 输出: 1, 2, 3

        List<Double> doubleList = Arrays.asList(1.1, 2.2, 3.3);
        printList(doubleList); // 输出: 1.1, 2.2, 3.3
    }
5、类型擦除 (Type Erasure)
  • 类型擦除 :Java 编译器会在编译时将泛型参数擦除为 Object 或其边界类型。

    编译后的class文件代码

    复制代码
     public static <T extends Number> void printList(List<T> list) {
            Iterator var1 = list.iterator();
    
            while(var1.hasNext()) {
                Number num = (Number)var1.next();//将T类型擦除为Number类型
                System.out.println(num);
            }
    
        }
    
        public static void main(String[] args) {
            List<Integer> intList = Arrays.asList(1, 2, 3);
            printList(intList);
            List<Double> doubleList = Arrays.asList(1.1, 2.2, 3.3);
            printList(doubleList);
        }
  • 类型擦除的限制:

    • 无法创建泛型类型实例(例如 T obj = new T();)。
    • 无法使用 instanceof 检测泛型类型。
    • 无法定义泛型数组(例如 T[] array = new T[10];)。
6、泛型方法与泛型类的区别
  • 泛型方法:可以定义在任何类中,作用范围为该方法。
  • 泛型类:类型参数定义在类名后,整个类中有效。
7、泛型的常见应用场景
  • 集合框架 :如 List<T>Map<K, V> 等,提升了集合的类型安全性。
  • 通用算法:对任意类型数据执行的通用算法,如排序和查找。
  • 实用工具类 :如 Optional<T>Future<T> 等,用于特定类型的操作。
相关推荐
聊天QQ:4877392781 小时前
手性超表面圆二色性comsol仿真 左右旋圆偏振光照射超表面的仿真, 圆二色性的计算
后端
木易 士心1 小时前
Element UI 多级菜单缩进的动态控制:从原理到工程化实践
前端·vue.js·ui
s***55811 小时前
如何使用Spring Boot框架整合Redis:超详细案例教程
spring boot·redis·后端
d***9351 小时前
Spring Boot 自动配置
java·spring boot·后端
h***67371 小时前
Spring Boot文件上传
java·spring boot·后端
狮子座的男孩1 小时前
js函数高级:03、详解原型与原型链(原型、显式原型与隐式原型、原型链、原型链属性、探索instanceof、案例图解)及相关面试题
前端·javascript·经验分享·显示原型与隐式原型·原型链及属性·探索instanceof·原型与原型链图解
Pr Young1 小时前
消息队列中的topic,partition,offset,broker,消费者组
后端
烛阴1 小时前
C#继承与多态全解析,让你的对象“活”起来
前端·c#
狗哥哥1 小时前
Swagger对接MCP服务:赋能AI编码的高效落地指南
前端·后端