一.泛型的引入
观察下列模拟实现顺序表的代码1:
public class MyArrayList {
private int[] array; // 保存顺序表的元素,即int类型的引用
private int size; // 保存顺序表内数据个数
public void add(int o) { //尾插代码 }
public int get(int index) { //获取 index 位置的元素代码 }
.....
}
上述代码实现的顺序表中只能存放int类型的元素,而想要保存指向Person或者Book类型对象的引用是不可以的,这种问题怎么解决?
其实解决方法就是将顺序表元素类型定义成Object类型,这样Object类型的引用就可以指向任何类型了,如代码示例2
①基类的引用可以指向子类的对象
②Object是Java中所有类的基类
示例代码2:
public class MyArrayList {
private Object[] array; // 保存顺序表的元素,即Object类型的引用
private int size; // 保存顺序表内数据个数
public void add(Object o) { //尾插代码}
public Object get(int index) { //获取 index 位置的元素代码 }
.....
}
修改后的代码就可以很自由的存储指向任意类型对象的引用了,如示例代码3。
示例代码3:
MyArrayList books = new MyArrayList();
for (int i = 0; i < 10; i++) {
books.add(new Book()); // 尾插 10 本书到顺序表
}
MyArrayList people = new MyArrayList();
for (int i = 0; i < 10; i++) {
people.add(new Person()); // 尾插 10 个人到顺序表
}
可是很快发现此时虽然可以添加任意类型的引用到里面,但还会出现一个问题,如果申请了一个顺序表空间,一号位置添加为Book类型,二号位置添加为Person类型,若用Book类型的引用来接收一号位置元素,强转之后显然是可以的,但是接收二号位置元素就不行了,此时就看出来这种方法的局限性了。
上述问题编译期间不会报错,可是运行时会显示错误,所以泛型这种机制就诞生了
①泛型可以增加编译期间的类型检查
②取消类型转换的使用
二.泛型类的使用
1.尖括号<> 是泛型的标志
2.E是类型变量(Type variable),变量名一般要大写
3.E在定义时是形参,代表的意思是MyArrayList最终传入的类型,现在还不知道
public class MyArrayList<E>{
private E[] array;
private int size;
....
}
注意:泛型类可以一次有多个类型变量,用逗号分割
1.泛型是作用在编译期间的一种机制,运行期间没有泛型的概念
2.泛型在运行期间,就是利用Object达到效果(不是特别准确)
示例代码:
// 定义了一个元素是 Book 引用的MyArrayList
MyArrayList<Book> books = new MyArrayList<Book>();
books.add(new Book());
// 会产生编译错误,Person 类型无法转换为 Book 类型
books.add(new Person());
// 不需要做类型转换
Book book = book.get(0);
// 不需要做类型转换
// 会产生编译错误,Book 类型无法转换为 Person 类型
Person person = book.get(0);
三.类型擦除
Java中所说的泛型其实是一种伪泛型。
示例代码:
getClass() 返回此 Object 的运行时类
实例化两个MyArrayList对象,分别打印出两个对象运行时类型信息
结果显示两个实例化在运行阶段类型都是MyArrayList类型,这就是伪泛型。就是在编译器编译期间有一个类型擦除(将E擦除,用Object替换),在运行期间是没有泛型概念的。