一、简介
1.1 什么是泛型
泛型是一种特殊的数据类型。 它是Java 的一个高级特性。在 Mybatis、Hibernate 这种持久化框架,泛型更是无处不在。
我们在定义一个语法结构时,有些东西不确定是什么类型,而是在使用期间确定,那么就不用指明具体类型,这样就可以使用泛型。 给形参赋值具体类型名。
一句话来说就是类型参数化,使用形参接收具体类型。
1.2 泛型的定义
泛型,定义在一对尖括号中,也是一个标识符,遵循大驼峰命名法。通常都是用一个大写字母。
public class Person<T>{}
public class Person<T,M>{}
public interface Calculate<T> {
public T calculate(T n1, T n2);
}
二、泛型的应用
2.1 泛型类的应用
泛型类:就是泛型应用在类上。一般在类名后,使用尖括号括起来。用大写字母作为泛型参数。
1)在类中使用泛型参数,来定义变量,因为要给变量初始化,所以构造器中的形参也应该规定为泛型类型。
public class Person<T>{
private T idCard;
public Person(T idCard){
this.idCard = idCard;
}
}
2)在实例化时,指定具体类型,泛型必须传引用类型,不能是八大基本数据类型。
public static void main(String[] args) {
//创建一个Person对象,需要给泛型参数赋值具体类型。
Person<String> p1 = new Person<String>("c1001");
Person<String> p2 = new Person<>("c1002");
//泛型参数只能赋值引用类型,不能赋值八大基本数据类型
Person<Long> p3 = new Person<>(10001L);
Date date = new Date();
Person<Date> p4 = new Person<>(date);
}
3)当一个子类继承带有泛型的父类时,一般情况下要给泛型参数赋值具体类型。
class Student extends Person<Integer>{
public Student(Integer idCard){
super(idCard);
}
}
4)子类的泛型参数可以赋值给父类的泛型参数。
下面就是将子类的E赋值给了父类的T:
class Teacher<E> extends Person<E>{
public Teacher(E idCard){
super(idCard);
}
}
5)在继承时,如果不指定具体类型,则默认是Object类型。
class President extends Person{
public President(Object idCard){
super(idCard);
}
}
小贴士:实例化过程中,可以只在一边给泛型参数赋值,但是两边的尖括号都不能省略。
2.2 泛型接口的应用
泛型接口,就是泛型应用在接口上,泛型定义在接口名后面,用<>括起来,用法与泛型类一致。
public interface MyComparable<T,M> {
public int mycompare(T o1, M o2);
public static void main(String[] args) {
Employee[] employees = new Employee[3];
employees[0] = new Employee("小张",18);
employees[1] = new Employee("小王",17);
employees[2] = new Employee("小李",19);
//使用比较器接口来重新定义比较规则 : 从泛型的角度来说,在实例化泛型接口时,要给泛型接口传 具体类型。
Comparator c = new Comparator<Employee>() {
//重写比较器里的compare方法
public int compare(Employee o1, Employee o2) {
//调用了自定义的员工类里的比较方法
return o1.mycompare(o1, o2);
}
};
Arrays.sort(employees,c);
System.out.println(Arrays.toString(employees));
}
}
子类实现接口:通常子类要给泛型接口的泛型参数赋值具体类型名。 下面案例,就是给T和M都赋值了Employee这个了类型:
class Employee implements MyComparable<Employee,Employee>{
String name;
int age;
public Employee(String name, int age) {
this.name = name;
this.age = age;
}
public String toString() {
return "["+name+","+age+"]";
}
//在我们自己定义的方法中来实现比较规则
public int mycompare(Employee o1, Employee o2) {
return o1.age - o2.age;
}
}
2.3 泛型方法的应用
泛型方法,就是泛型应用在方法上。需要把泛型定义在返回值类型前面。
泛型方法在调用期间,不需要指定具体类型,只需要传入具体对象,编译器会自动推断对象的类型。
案例演示:定义一个工具类,用于比较两个对象长得是否一样。
public class MyUtil {
//泛型方法,泛型定义在返回值类型前面。
public static <T> boolean equals(T t1,T t2){
return t1.equals(t2);
}
}
class Cat {
String name;
public Cat(String name) {
this.name = name;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Cat cat = (Cat) o;
return Objects.equals(name, cat.name);
}
@Override
public int hashCode() {
return Objects.hashCode(name);
}
}
测试:
public static void main(String[] args) {
Cat c1 = new Cat("小黄");
Cat c2 = new Cat("小黄");
//泛型方法在调用期间,不需要指定具体类型,只需要传入具体对象,编译器会自动推断对象的类型
//泛型方法调用期间,并没有给泛型参数赋值。下面的案例是c1给t1,c2给t2赋值,没有给T赋值。
boolean equals = MyUtil.equals(c1, c2);
System.out.println("equals: " + equals);
}
2.4 泛型通配符
2.4.1 简介
泛型通配符用?表示,代表不确定的类型,是泛型的一个重要组成。在调用时,表示我不关心具体类型。也可以使用通配符规定调用时,传入的类型的范围,即上边界,和下边界。
//不管List集合里面装的是什么类型,只要传进来的是List集合就可以正常运行。
public static void print(List<?> lists){
for(int i = 0;i < lists.size();i++){
System.out.println(lists.get(i));
}
}
测试:
public static void main(String[] args) {
List<Integer> nums = new ArrayList<Integer>();
nums.add(1);
nums.add(2);
nums.add(3);
MyUtil.print(nums);
List<Long> ns = new ArrayList<Long>();
ns.add(1L);
ns.add(2L);
ns.add(3L);
MyUtil.print(ns);
}
运行结果:
1
2
3
1
2
3
2.4.2 上边界
上边界的定义: <? extends 具体类名>
具体调用的时候,可以是上边界的任何子类或者是上边界本身类型。
public static void print2(List<? extends Number> lists) {
for(int i = 0;i < lists.size();i++){
System.out.println(lists.get(i));
}
}
上边界的测试:
print2(new ArrayList<Long>());//Number的子类型,可以。
print2(new ArrayList<Number>());//本类型,可以。
print2(new ArrayList<Object>());//不可以,最高只能到达Number类型,而Object是Number的父类型。
2.4.3 下边界
下边界的定义: <? super 具体类名>
具体调用的时候,可以是下边界的任何父类或者是本类型。
public static void print3(List<? super Integer> lists) {
for(int i = 0;i < lists.size();i++){
System.out.println(lists.get(i));
}
}
下边界的测试:
print3(new ArrayList<Number>());//Number是Integer的父类,可以。
print3(new ArrayList<Long>());//Long和Integer没关系,因此不可以。
print3(new ArrayList<Object>());//Object是Integer的父类,可以。
2.5 总结
泛型是一种特殊类型,你可以把它用在类、接口、方法上,从而实现一些通用算法。
此外,使用泛型有三个步骤:定义类型变量、使用类型变量、确定类型变量。
在确定类型变量时,你可以用泛型通配符来限制泛型的范围,从而实现一些特殊算法。