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

🐂🐎

相关推荐
不吃香菜学java8 小时前
Redis的java客户端
java·开发语言·spring boot·redis·缓存
captain3768 小时前
事务___
java·数据库·mysql
北漂Zachary9 小时前
四大编程语言终极对比
android·java·php·laravel
小江的记录本9 小时前
【网络安全】《网络安全常见攻击与防御》(附:《六大攻击核心特性横向对比表》)
java·网络·人工智能·后端·python·安全·web安全
贵沫末9 小时前
python——打包自己的库并安装
开发语言·windows·python
文祐9 小时前
C++类之虚函数表及其内存布局(一个子类继承一个父类)
开发语言·c++
zuowei28899 小时前
华为网络设备配置文件备份与恢复(上传、下载、导出,导入)
开发语言·华为·php
xiaohe0710 小时前
超详细 Python 爬虫指南
开发语言·爬虫·python
嗑嗑嗑瓜子的猫10 小时前
Java!它值得!
java·开发语言
xiaoshuaishuai810 小时前
C# GPU算力与管理
开发语言·windows·c#