【从零开始学Java | 第二十三篇】泛型(Generics)

前言

在上一篇讲 List 集合时,我们代码里一直写着 <String><Integer>,其实这就是泛型

很多新手都会把泛型当成死记硬背的语法,包括我也一样,但是实际上,它是为了保护程序员不犯错而出现的一个机制。

一、为什么需要泛型

JDK5之前,是没有泛型这个概念的,集合默认是可以放入任何类型的元素的。

我们写一个没有泛型的Demo:

当我们想调用String类型的length方法时,由于多态无法访问子类的特有功能,所以会报错,那么我如果进行类型的强制转换呢?会发生什么?

报错ClassCastException

结论:

如果我们没有给集合指定数据类型,默认所有数据类型为Object,那么这个集合就可以存储任何类型的数据,因此带来了一个坏处,我们在获取了数据之后,无法使用该特定数据类型的方法

在我们加上泛型后,如果我们想往集合中添加非泛型指定类型的数据时,编译都无法通过。

泛型的细节

  • 泛型中不能写基本数据类型,因为Java中的泛型为伪泛型,在编译好后,后台还是以Object的类型来存储的,而基本数据类型如int、double等无法强转为Object,因此只能写基本数据类型对应的包装类
  • 指定泛型的具体类型后,传递数据时,可以传入该类类型或者其子类类型
java 复制代码
ArrayList<Animal> al = new Arraylist<>();

al.add(new Animal());
al.add(new Dog());
al.add(new Cat());
  • 如果不写泛型,类型默认是Object

二、泛型的使用

泛型可以在很多地方进行定义。

  1. 定义在类后面,称之为泛型类
  2. 定义在方法上面,称之为泛型方法
  3. 定义在接口后面,称之为泛型接口

1.泛型类

在定义一个类的时候,某个变量的数据类型不确定时,就可以使用带有泛型的类。

格式:

修饰符 class 类名<类型>{

}

例:

public class ArrayList<E>{

}

此处E可以理解为变量,但是不是用来记录数据的,而是记录数据的类型,可以写成T、E、K、V等

具体使用:

泛型类的书写:

java 复制代码
import java.util.Arrays;

public class MyArrayList <E>{
    Object[] obj = new Object[10];
    int size;

    public boolean add(E e){
        obj[size] = e;
        size++;
        return true;
    }

    public E get(int index){return (E)obj[index];}

    @Override
    public String toString() {
        return Arrays.toString(obj);
    }
}

泛型类的使用:

java 复制代码
public class GenericsTest02 {
    public static void main(String[] args) {
        MyArrayList<String> mal = new MyArrayList<>();
        mal.add("张三");
        mal.add("李四");
        mal.add("王五");

        System.out.println(mal);
    }
}

运行结果:

2.泛型方法

方法中形参类型不确定时,

  1. 可以使用类名后面定义的泛型,所有类中的方法都可以使用这个泛型。
  2. 也可以在方法声明上定义自己的泛型,仅在声明的这个方法中可以使用。

格式:

修饰符<类型> 返回值类型 方法名(类型 变量名){

}

举例:

public<T> boolean get(T t){

}

定义一个泛型方法:

java 复制代码
public final class ListUtils {
    private ListUtils(){}
    
    public static<E> void addAll(ArrayList<E> al, E e1, E e2, E e3){
        al.add(e1);
        al.add(e2);
        al.add(e3);
    }
}

拓展:上面的addAll方法只能添加确定个数的元素,但是我不确定要添加的元素,应该如何实现?

java 复制代码
public final class ListUtils {
    private ListUtils(){}

    public static <E> void addAll(ArrayList<E> al, E...e){
        for (E e1 : e) {
            al.add(e1);
        }
    }
}

3.泛型接口

格式:

修饰符 interface 接口名<类型>{

}

举例:

public interface List<E>{

}

如何使用一个带泛型的接口?

方式一:实现类给出具体类型。

方式二:实现类延续泛型,在创建对象时再确定具体类型。

①实现类给出具体类型

②实现类延续泛型,在创建对象时再确定具体类型

总结

  1. **为什么用泛型?**把运行时的报错(ClassCastException)提前到编译期拦截,避免手写强制类型转换。
  2. **如何用?**掌握 <T> 在类、接口和方法上的定义语法。
  3. **底层原理?**泛型擦除,运行时全都是 Object

🐂🐎

相关推荐
曹牧3 分钟前
PostMan:400 Bad Request‌
java
devpotato12 分钟前
RPO与RTO:容灾的两个关键指标
java·后端
宁渡AI大模型28 分钟前
AI 全栈面试新趋势:Vibe Coding、前端、Java 后端高频面试题深度解析|河南宁渡科技有限公司编程教程
java·javascript·人工智能·python·ai大模型
CallFay云起未来39 分钟前
AI客服如何与人工客服协同?从任务路由到上下文交接的Agent架构实践
java·人工智能·文心一言
cfm_291441 分钟前
观察者模式
java
Data_Journal1 小时前
如何将网页抓取用于机器学习
大数据·开发语言·数据库·python·scrapy
野生技术架构师1 小时前
1000+ Java面试题知识图谱:从基础语法到分布式架构的完整拓扑
java·面试
步行cgn1 小时前
Spring 底层如何创建对象:反射机制与实例化策略
java·后端·spring
郝学胜-神的一滴1 小时前
Effective Python 条款 13:善用星号解包,告别下标切片拆分的坑
服务器·开发语言·数据结构·python·程序人生·pycharm
一水鉴天1 小时前
词、术语、概念:从特征集收束到统一拓扑变换的升格与降格模型 20260915(豆包)
开发语言·人工智能