泛型概念
泛型,即“参数化类型”。一提到参数,最熟悉的就是定义方法时有形参,然后调用此方法时传递实参。那么参数化类型怎么理解呢?
顾名思义,就是将类型由原来的具体的类型参数化,类似于方法中的变量参数,此时类型也定义成参数形式(可以称之为类型形参),
然后在使用/调用时传入具体的类型(类型实参)。
泛型的本质是为了参数化类型(在不创建新的类型的情况下,通过泛型指定的不同类型来控制形参具体限制的类型)。也就是说在泛型使用过程中,
操作的数据类型被指定为一个参数,这种参数类型可以用在类、接口和方法中,分别被称为泛型类、泛型接口、泛型方法。
泛型的引入
- 问题:
比如我们实现一个顺序表,只能保存int类型的元素,如果我们想要保存指向Person类型对象的引用,又或者想要保存一个Book对象类型的引用,如何解决?
- 回答
我们很在然的就会想到将我们的元素类型定义为Object类型,那么Object类型的引用就可以指向Personl类型的对象或者Book类型的对象
public class MyArrayList {
private Object[] array; // 保存顺序表的元素,即Object 类型的引用
private int size; // 保存顺序表内数据个数
public void add(Object o) { 尾插 }
public Object get(int index) { 获取 index 位置的元素 }
这样我们就可以很自由的将任意对象引入到我们的顺序表
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 个人到顺序表
}
现在MyArrayList可以添加任意类型的引用到其中了但会产生以下问题
MyArrayList books = new MyArrayList(); books.add(new Book);
// 将 Object 类型转换为 Person 类型,需要类型转换才能成功
// 这里编译正确,但运行时会抛出异常 ClassCastException
Person person = (Person)books.get(0));
因此,为了
- 增加编译期间的类型检查
- 取消类型转换的使用
泛型就此诞生
泛型的分类
定义:
<>是泛型的标志
//E是类型变量名 变量名一般大写
public static <E> 返回值类型 方法名称(参数列表){
.......
}
调用:
类名.<实际类型> 方法名称(参数列表)
类名. 方法名称(参数列表)
注意:调用时泛型可以省络。编译器会自动识别
泛型实现一个对象冒泡排序
Teacher类
public class Teacher {
public String name;
public int age;
public int height;
public Teacher(String name, int age, int height) {
this.name = name;
this.age = age;
this.height = height;
}
@Override
public String toString() {
return "Teacher{" +
"name='" + name + '\'' +
", age=" + age +
", height=" + height +
'}';
}
}
TeacherByAgeCompartor类(实现按年龄比较的比较器)
import java.util.Comparator;
public class TeacherByAgeCompartor implements Comparator<Teacher> {
@Override
public int compare(Teacher o1, Teacher o2) {
return o1.age-o2.age;
}
}
GenericMethodDemo类(实现泛型魔炮排序)
import java.util.Arrays;
import java.util.Comparator;
public class GenericMethodDemo {
//泛型定义冒泡排序
public static <T>void bubbleSort(T[] array, Comparator<T> comparator){
for (int i = 0; i < array.length - 1; i++) {
for (int j = 0; j < array.length - i - 1; j++) {
// T 实际上是什么类型
// Teacher/Student 引用类型
int r = comparator.compare(array[j], array[j + 1]);
if (r > 0) {
// 代表 array[j] 指向的对象 "大于" array[j + 1] 指向对象
T t = array[j];
array[j] = array[j + 1];
array[j+1] = t;
}
}
}
}
public static void main(String[] args) {
Teacher[] teachers = {
new Teacher("张三", 18, 180),
new Teacher("李四", 34, 190),
new Teacher("王五", 16, 200),
new Teacher("六子", 16, 170)
};
Comparator<Teacher> byAge = new TeacherByAgeCompartor();
//泛型方法调用
GenericMethodDemo.<Teacher>bubbleSort(teachers, byAge);
//也可以不写编译器会识别
GenericMethodDemo.bubbleSort(teachers, byAge);
System.out.println(Arrays.toString(teachers));
}
}
//<>是泛型的标志
//E是类型变量名 变量名一般大写
public class MyArrayList<E> {
private E[] array;
private int size;
...
}
注意: 泛型类可以一次有多个类型变量,用逗号分割。
- 泛型是作用在编译期间的一种机制,即运行期间没有泛型的概念。
- 泛型代码在运行期间,就是我们上面提到的,利用 Object 达到的效果
泛型总结
- 泛型是为了解决某些容器、算法等代码的通用性而引入,并且能在编译期间做类型检查。
- 泛型利用的是 Object 是所有类的祖先类,并且父类的引用可以指向子类对象的特定而工作。
- 泛型是一种编译期间的机制,即 MyArrayList 和 MyArrayList 在运行期间是一个类型。
- 泛型就是java中的一种合法语法标志就是<>
- 泛型中的类型变量一定是引用数据类型不能是基本数据类型
- 泛型类种不能包含有泛型参数的静态属性和方法
class A<T>{
T a;//正确
void method(T b){...}//正确
static T c;//错误
static void method(T d){...}//错误
}
T是在实例化一个对象确定的,静态属性和方法中也就不能带泛型参数,不依赖于对象,