【从零开始学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

🐂🐎

相关推荐
码云数智-大飞2 小时前
Java接口与抽象类:从本质区别到架构选型
开发语言
m0_750580302 小时前
Java并发—Java线程
java·开发语言
我不是懒洋洋2 小时前
预处理详解
c语言·开发语言·c++·windows·microsoft·青少年编程·visual studio
QuZero2 小时前
JDK7 ConcurrentHashMap principle
java·哈希算法
计算机安禾2 小时前
【数据结构与算法】第14篇:队列(一):循环队列(顺序存储
c语言·开发语言·数据结构·c++·算法·visual studio
独断万古他化2 小时前
【Java 实战项目】多用户网页版聊天室:消息传输模块 —— 基于 WebSocket 实现实时通信
java·spring boot·后端·websocket·ajax·mybatis
yyt3630458412 小时前
spring单例bean线程安全问题讨论
java·spring
weixin_649555673 小时前
C语言程序设计第四版(何钦铭、颜晖)第十一章指针进阶之奇数值结点链表
c语言·开发语言·链表
书到用时方恨少!3 小时前
Python os 模块使用指南:系统交互的瑞士军刀
开发语言·python