泛型是一种特殊的数据类型。它是Java 的一个高级特性。在 Mybatis、Hibernate 这种持久化框架,泛型更是无处不在。
在之前,不管我们在定义成员变量时,还是方法的形参时,都要规定他们的具体类型。
int n1;
String n2;
确定了n1为int类型,n2为String类型。这样参数类型一旦确定就传不了其他类型的值了
这样很不灵活,那么就产生了泛型。
即,我们在定义一个语法结构时,不用指明具体类型,而是先定义一个类型变量,在真正使用的时候再确定该变量的具体类型。
一句话:就是类型参数化。
数据参数化: 就是使用形参接收具体数据(实际参数)
类型参数化: 就是使用形参接收具体类型。
泛型,定义在一对尖括号中,也是一个标识符,遵循大驼峰命名法。通常都是用一个大写字母。比如:
public class Person
{}
public class Person{}
public interface Calculate{
public T calculate(T n1, T n2);
}
当我们将泛型用在类上时,这个类就叫做泛型类。 泛型定义在类名后。
public class Person{//在类上定义泛型的类型的变量,T,M代表未来将要使用的具体类型 T name; M age; public Person(){} public Person(T name ,M age){//初始化 this.name =name; this.age =age; } } 在实例化时,指定具体类型
public static void main(String[] args) { Personb=new Person<>("lily",10); } } 在继承时,指定具体类型
public class Student extends Person{ Student (String name,Short age){ super(name,age); } 在继承时,如果不指定具体类型,则默认是Object类型
当我们将泛型用在接口上时,这个接口就叫做泛型接口。 泛型定义在接口名后。 用法和泛型类,一模一样。
public interface MyComparator
{
int compare(T t,M m);
T calculate(T t,M m);
}实例化时
public static void main(String[] args) { MyComparator comparator = new MyComparator() { public int compare(Integer t1, Integer t2) { return t1.compareTo(t2); } public Integer calculate(Integer t1, Integer t2) { return t1+t2; } }; } 子类实现接口时
//实现子类时,不指定具体泛型,默认就是Object类型了。 class Teacher implements MyComparator{ @Override public int compare(Object o, Object o2) { //两个teacher在比较时,要不要强转,思考一下,会不会很麻烦。 return 0; } @Override public Object calculate(Object o, Object o2) { return null; } }
泛型不仅能用在类和接口上,还可以用在方法上。 需要把泛型定义在返回值类型前面。
public class MyUtil {
//泛型方法,泛型定义在返回值类型前面。
public staticboolean eq(T a, T b) {
return a.equals(b);
}
}public class Employee{
String name;
public Employee(String name){
this.name = name;
}@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Employee employee = (Employee) o;
return Objects.equals(name, employee.name);
}
}测试:
public static void main(String[] args) {
//调用泛型方法时,不需要传入具体类型名,只需要传入具体类型的值
//会主动推导出具体的类型。
boolean f = MyUtil.eq("abc","hello");
System.out.println("f = " + f);Employee e1 = new Employee("张三");
Employee e2 = new Employee("张三");f = MyUtil.eq(e1,e2);
System.out.println("f = " + f);
}
泛型通配符用 ?,代表不确定的类型,是泛型的一个重要组成。 在调用时,表示我不关心具体类型。
也可以使用通配符规定调用时,传入的类型的范围,即上边界,和下边界。
比如,你要写一个通用方法,把传入的 List 集合输出到控制台,那么就可以这样做。
public class Application {
public static void print(List> list) {
for (int i = 0; i < list.size(); i++) {
System.out.println(list.get(i));
}
}public static void main(String[] args) {
// Integer 集合,可以运行
ListintList = Arrays.asList(0, 1, 2);
print(intList);// String 集合,可以运行
ListstrList = Arrays.asList("0", "1", "2");
print(strList);
}
}
List> list
代表我不确定 List 集合装的是什么类型,有可能是 Integer,有可能是 String,还可能是别的东西。但我不管这些,你只要传一个 List 集合进来,这个方法就能正常运行。
上边界,代表类型变量的范围有限,只能传入某种类型,或者它的子类。
利用 extends 类名>
的方式,可以设定泛型通配符的上边界。
public class TopLine {
public static void print(List extends Number> list) {
for (int i = 0; i < list.size(); i++) {
System.out.println(list.get(i));
}
}public static void main(String[] args) {
// Integer 是 Number 的子类,可以调用 print 方法
print(new ArrayList()); // String 不是 Number 的子类,没法调用 print 方法
print(new ArrayList());
}}
你想调用 print()
方法中,那么你可以传入 Integer 集合,因为 Integer 是 Number 的子类。但 String 不是 Number 的子类,所以你没法传入 String 集合。
下边界,代表类型变量的范围有限,只能传入某种类型,或者它的父类。
利用 super 类名>
的方式,可以设定泛型通配符的下边界
public class LowLine {
public static void print(List super Integer> list) {
for (int i = 0; i < list.size(); i++) {
System.out.println(list.get(i));
}
}public static void main(String[] args) {
// Number 是 Integer 的父类,可以调用 print 方法
print(new ArrayList()); // Long 不是 Integer 的父类,没法调用 print 方法
// print(new ArrayList());
}
}
你想调用 print()
方法中,那么可以传入 Number 集合,因为 Number 是 Integer 的父类。但 Long 不是 Integer 的父类,所以你没法传入 Long 集合。