1.泛型的简介
泛型是一种特殊的的数据类型,它是Java 的一个高级特性。在 Mybatis、Hibernate 这种持久化框架,泛型更是无处不在。
泛型总结下来就是:类型参数化
定义期间,有些东西不确定是什么类型,在使用期间确定, 这样就可以使用泛型。 给形参赋值具体类型名
数据参数化: 就是使用形参接收具体数据(实际参数)
类型参数化: 就是使用形参接收具体类型。
2.泛型的定义
泛 型,定义在一对尖括号中,也是一个标识符,遵循大驼峰命名法。通常都是用一个大写字母
泛型参数,只能赋值引用类型,不能赋值八大基本数据类型
案例:
public class Person<T>{}
public class Person<T,M>{}
public interface Calculate<T> {
public T calculate(T n1, T n2);
}
3.泛型的应用
(1)泛型类的应用:
一般用在类名后,使用尖括号括起来。用大写字母作为泛型参数。
(1)定义一个泛型类:
public class Person<T> {
private T idCard;
public Person(T idCard){
this.idCard = idCard;
}
public static void main(String[] args) {
//创建一个Person对象,需要给泛型参数赋值具体类型
Person p = new Person<String>("c1001");
Person<String> p2 = new Person<>("c1001");
// 泛型参数,只能赋值引用类型,不能赋值八大基本数据类型。
Person<Long> p3 = new Person<>(100001L);
Date date = new Date();
// 小贴士: 实例化过程中,可以只在一边给泛型参数赋值,但是两边的尖括号都不能省略
Person<Date> p4 = new Person<>(date);
}
}
小贴士: 实例化过程中,可以只在一边给泛型参数赋值,但是两边的尖括号都不能省略
(2)当一个子类继承带有泛型的父类时,一般情况下要给泛型参数赋值具体类名
class Student extends Person<Integer>{
public Student(Integer idcard){
super(idcard);
}
}
(3)在继承过程中,子类也可以有自己的泛型参数,即把子类的泛型参数赋值给父类的泛型参数
下面例子就是E赋值了T。
class Teacher<E> extends Person<E>{
public Teacher(E idCard){
super(idCard);
}
}
(4)如果子类在定义期间,没有给泛型父类的泛型参数赋值,那么默认就是Object类型
class President extends Person{
public President(Object idCard){
super(idCard);
}
(2)泛型接口:泛型用在接口上
用法和泛型类,一模一样。
1.定义一个泛型接口
public interface MyComparable<T,M> {
/**
* 两种类型进行比较
* @param o1
* @param o2
* @return
*/
public int mycompare(T o1, M o2);
}
2.子类实现接口: 通常子类要给泛型接口的泛型参数赋值具体类型名(如果不指定具体泛型参数,默认就是Object类型)
下面案例,就是给T和M 都赋值了Employee这个类型,两个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;
}
}
测试:
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);
//使用比较器接口,来重新定义比较规则 : 从泛型的角度来说,在实例化泛型接口时,要给泛型参数传具体类型 ,这里传的是Employee类型
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));
}
(3)泛型方法的应用:位置位于返回值类型的前面
案例演示:定义一个工具类,用于比较两个对象长得是否一样。
public class MyUtil {
public static <T> boolean equals(T t1,T t2){
return t1.equals(t2);
}
}
定义一个Ca类型:要实现两个对象的比较,必须重写equals和hashCode,
class Cat{
String name;
public Cat(String name) {
this.name = name;
}
//要实现两个对象的比较,必须重写equals和hashCode,即默认的比较规则不适用了。
@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);
}
4.泛型通配符 :?
泛型通配符用 ?
表示,代表不确定的类型,是泛型的一个重要组成。 在调用时,表示我不关心具体类型。
1.简单的应用,设置一个方法实现把集合元素打印到控制台,不确定是什么类型的
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);//1 2 3//打印成功
List<Long> ns = new ArrayList<Long>();
ns.add(1L);
ns.add(2L);
ns.add(3L);
MyUtil.print(ns);// 1 2 3//打印成功
}
也可以使用通配符规定调用时,传入的类型的范围,即上边界,和下边界。
1.上边界
上边界的定义: <? extends 具体类名>
具体使用的时候,可以是上边界的任何子类型或者是本类型
public static void print2(List<? extends Number> list){
for (int i = 0; i < list.size(); i++) {
System.out.println(list.get(i));
}
}
测试:最高只能是Number类型
//上边界的测试:
print2(new ArrayList<Long>());
print2(new ArrayList<Number>());
//print2(new ArrayList<Object>()); 最高到达Number类型
2.下边界
下边界的定义:<? super 具体类名>
具体使用的时候,可以是下边界的任何父类型或者本类型
public static void print3(List<? super Integer> list){
for (int i = 0; i < list.size(); i++) {
System.out.println(list.get(i));
}
}
测试:最低只能是Interger类型
//下边界的测试:
print3(new ArrayList<Integer>());
print3(new ArrayList<Number>());//Number 是 Integer 的父类
print3(new ArrayList<Object>());
//print3(new ArrayList<Long>()); Long和Integer没有关系。
使用泛型有三个步骤:定义类型变量、使用类型变量、确定类型变量。
在确定类型变量这一步中,可以用泛型通配符来限制泛型的范围,从而实现一些特殊算法。