一、没有推出泛型概念之前的程序

`
// 创建一个没有定义泛型的数组
ArrayList apple = new ArrayList();

// 该数组看似可以添加任何的类型的元素,但实际是因为apple数组将添加的元素统一看成了Object类型;
// 此时的数组看似很方便,什么类型的元素都可以添加,因为它们统统都是Object的子类;
apple.add("abc");
apple.add(123);

// 当获取数组中的元素时,Java语言也是默认使用Object类型的变量来接收;
Object o = apple.get(0); // 此时是没有问题的;
System.out.print(o);

// 可是一旦想要获取元素具体的数据类型,用于操作,就会涉及到类型转换(即将Object类型转换为实际操作的类型);
// 如下强转为Integer类型,此时编译并不会有问题,但一旦运行就会报:类型转换的错误(java.lang.ClassCastException: class java.lang.String cannot be cast to class java.lang.Integer)
Integer a =(Integer)apple.get(0);
System.out.print(a);

// 此时你可能会说,你明明第一个元素存储的是字符串,当然转型Integer会报错啊!
// 嗯呢,是的,那按照您的意思就是,每当我们自己获取数组元素的时候,还要明确记得我们当时存储元素的数据类型;
String b = (String)apple.get(0);// 这样分别操作确实不会报错;
Integer a =(Integer)apple.get(1);
System.out.print(a);

// 但这也是问题的所在,如果我们数组元素很多呢,记不过来难免会发生错误;

`

二、泛型应运而生

为了解决上述遇到的问题,JDK1.5中,推出了泛型的概念。泛型提供了编译时类型安全的监测机制,即在编译时就能检查到非法的数据类型结构。
// 首先了解一下泛型的样子
// 定义一个带有泛型概念的数组
ArrayList<T> apple = new ArrayList<>(); // T:可以理解为形参。后续你可能还能见到<E> <M>...可理解为一个意思,大概有T、E、M、K、V

// 那定义一个String类型的数组呢?
// 如下,想定义什么类型的数组,就把形参 T 换成实参;
ArrayList<String> apple = new ArrayList<>();

// 那就添加元素吧,你会发现:此时的apple数组只接收String类型的元素,添加其它类型的元素就会报错;
apple.add("abc");// 此时add方法也会提示你传递String类型的元素;
apple.add(123);// 此时添加int类型的元素就会有报错提示,是添加不上的;

// 那获取元素呢?你会发现:使用了泛型定义的数组在获取元素时,java语言不再默认使用Object类型的变量接收了,而是你定义的类型
String a = apple.get(0);

泛型的好处:

  • 在编译时期就可以检查出数据类型的错误;
  • 泛型的本质就是参数化类型,也就是类型参数化;
  • 因为在存储时就已经指定了存储元素的类型,所以在获取使用时就消除了强制类型的转换;