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

🐂🐎

相关推荐
Alice-YUE3 分钟前
【无标题】
开发语言·javascript·ecmascript
阿丘Akiu19 分钟前
Linux部署我的世界服务器
java
折哥的程序人生 · 物流技术专研23 分钟前
《Java面试85题图解版(二)》进阶深化中篇:Spring核心 + 数据库进阶
java·后端·spring·面试
叼烟扛炮40 分钟前
C++ 知识点17 友元
开发语言·c++·算法·友员
SamDeepThinking1 小时前
写代码不考虑前后兼容,迟早要还的
java·后端·程序员
计算机安禾1 小时前
【c++面向对象编程】第2篇:类与对象(一):定义第一个类——成员变量与成员函数
开发语言·c++
Dxy12393102161 小时前
Python Pillow库:`img.format`与`img.mode`的区别详解
开发语言·python·pillow
亿牛云爬虫专家1 小时前
深度解析:数据采集场景下的 Java 代理技术实战
java·开发语言·数据采集·动态ip·动态代理·代理配置·连接池复用
小小仙。1 小时前
IT自学第四十二天
java·开发语言
java1234_小锋1 小时前
说一下Spring的事务传播行为?
java·数据库·spring