Java - 泛型 - generic
前言 : 泛型的好处
- 编译时,检查添加元素的类型,提高安全性
- 减少了类型转换的次数,提高效率
一、泛型介绍
- 泛型又称为参数化类型,JDK5.0新增特性,解决数据类型的安全性问题
- 在类声明或实例化时只要指定好需要的具体的类型即可
- Java泛型可以保证如果程序在编译时没有发出警告,运行时就不会产生
ClassCastException异常。同时代码更加简洁、健壮。 - 泛型的作用是:可以在类声明时通过一个标识表示类中某个属性的类型,或者是某个方法的返回值类型,或者是参数类型。
二、泛型类的结构
下面是泛型类的一个简单结构示例:
// Person.java中
/**
* 一个泛型类示例,声明的时候传入的类型
*/
public class Person<E> {
/**
* 可以用作变量的类型
*/
private E s;
/**
* 可以用作方法的返回值
* @return 当前的泛型对象
*/
public E getS() {
return s;
}
/**
* 可以用作方法的形参
* @param e 当前的泛型对象
*/
public void setS(E e){
this.s = e;
}
}
验证是否是传入的类型:
// main.java中
Person<String> stringPerson = new Person<>();
stringPerson.setS("123"); // 可以传入
stringPerson.setS(123); // 报错
System.out.println(stringPerson.getS() instanceof String);
// 控制台输出
// true
控制台输出:true
说明是对应的String类型
三、泛型的语法
1、泛型的声明
- 泛型接口
interface接口名<T>{}
- 泛型类
class类名<K,V>{}
- 说明:
- 其中,T,K,V不代表值,而是表示类型
- 任意字母都可以。常用T表示,是Type的缩写
2、泛型的实例化
- 在尖括号中写入你想要的类型
Person<String> stringPerson = new Person<>();
四、泛型使用注意事项
-
泛型里面的T,E只能是引用类型
-
ArrayList<Integer> integers = new ArrayList<Integer>(); // 正确 ArrayList<int> integers2 = new ArrayList<int>(); // 报错
-
-
在指定泛型具体类型后,可以传入该类型或者其子类类型
// Son 类中 public class Son extends Person<String>{ }// Test 类中 public class Test { }// Main.java中 ArrayList<Person<String>> people = new ArrayList<>(); Son son = new Son(); Test test = new Test(); people.add(son); // 可以传入 people.add(test); // 报错 -
如果我们不往泛型的<>中写入值,那么默认值就是object
五、泛型题目案例
定义
Employee类
该类包含:
private成员变量name,sal,birthday,其中birthday为MyDate类的对象;为每一个属性定义
getter,setter方法;重写
toString方法输出name,sal,birthday;
MyDate类包含 :private成员变量month,day,year;并为每一个属性定义getter,setter方法;创建该类的3个对象,并把这些对象放入
ArrayList集合中 (集合用泛型定义),对集合中的元素进行排序,并遍历输出排序方式:调用
ArrayList的sort方法,传入Comparator对象 (使用泛型) ,先按照name排序,如果name相同,则按生日日期的先后排序。【即:定制排序】
MyDate类import java.util.StringJoiner; /** * 存储出生日期的对象 */ public class MyDate implements Comparable<MyDate>{ private Integer year; private Integer month; private Integer day; public MyDate(Integer year, Integer month, Integer day) { this.year = year; this.month = month; this.day = day; } public Integer getMonth() { return month; } public void setMonth(Integer month) { this.month = month; } public Integer getDay() { return day; } public void setDay(Integer day) { this.day = day; } public Integer getYear() { return year; } public void setYear(Integer year) { this.year = year; } @Override public String toString() { return new StringJoiner(", ", MyDate.class.getSimpleName() + "[", "]") .add("year=" + year) .add("month=" + month) .add("day=" + day) .toString(); } @Override public int compareTo(MyDate o) { // 在比较生日年月日 int yearMinus = year - o.getYear(); if(yearMinus != 0){ return yearMinus; } int monthMinus = month - o.getMonth(); if(monthMinus != 0){ return monthMinus; } return day - o.getDay(); } }
Employee类import java.sql.Time; import java.time.LocalDateTime; import java.util.StringJoiner; /** * 员工对象 */ public class Employee { private String name; private Integer sal; private MyDate birthday; public Employee() { } public Employee(String name, Integer sal, MyDate birthday) { this.name = name; this.sal = sal; this.birthday = birthday; } public String getName() { return name; } public void setName(String name) { this.name = name; } public Integer getSal() { return sal; } public void setSal(Integer sal) { this.sal = sal; } public MyDate getBirthday() { return birthday; } public void setBirthday(MyDate birthday) { this.birthday = birthday; } @Override public String toString() { return new StringJoiner(", ", Employee.class.getSimpleName() + "[", "]") .add("name='" + name + "'") .add("sal=" + sal) .add("birthday=" + birthday) .toString(); } }
Main类import com.hgz.javaDemo.generic.model.ComparatorTest; import com.hgz.javaDemo.generic.model.Employee; import com.hgz.javaDemo.generic.model.MyDate; import javax.lang.model.element.VariableElement; import java.util.ArrayList; import java.util.Comparator; /** * 入口类 */ public class Main { public static void main(String[] args) { Employee employee1 = new Employee("小罗",2500,new MyDate(2024,11,12)); Employee employee2 = new Employee("小李",5000,new MyDate(2024,11,14)); Employee employee3 = new Employee("小李",4500,new MyDate(2024,11,13)); ArrayList<Employee> employees = new ArrayList<>(); employees.add(employee1); employees.add(employee2); employees.add(employee3); // 使用 Comparator 按年龄排序 Comparator<Employee> ageComparator = new Comparator<Employee>() { @Override public int compare(Employee emp1, Employee emp2) { if(!(emp1 instanceof Employee && emp2 instanceof Employee) ){ return 0; } // 先比较名字 int i = emp1.getName().compareTo(emp2.getName()); if(i != 0){ return i; } return emp1.getBirthday().compareTo( emp2.getBirthday()); } }; employees.sort(ageComparator); for (Employee employee : employees) { System.out.println(employee); } } }
六、自定义泛型类
/**
* 1、Tiger 后面泛型,所以我们把Tiger 就称为自定义泛型类
* 2、T,R,M 泛型的标识符,一般是单个大写字母
* 3、泛型标识符可以有多个
*/
public class Tiger<T,R,M> {
String name;
R r;
M m;
T t;
}
自定义泛型使用细节:
- 普通成员可以使用泛型(属性,方法)
- 使用泛型的数组,不能初始化
- 因为数组在new的时候,不能确定当前泛型是什么类型,所以无法在内存空间中开辟
- 静态方法和属性中不能使用类的泛型
- 因为
static是与类相关的,在类加载的时候,对象还没有创建,所以也无法确定类型
- 因为
- 泛型类的类型,是在创建对象时确定的(因为创建对象时,需要指定确定类型)
- 如果在创建对象时,没有指定类型,默认是
Object
七、自定义泛型接口
/**
* 泛型接口定义
*/
public interface IUsb<U,R> {
U u; // 无法使用报错
//普通方法中可以使用泛型接口
R get(U u);
void hi(R r);
void run(R r1,R r2,U u1,U u2);
// 在jdk8 中,可以在接口中,使用默认方法,也是可以使用泛型
default R method(U u){
return null;
}
}
自定义泛型使用细节:
-
接口中,静态成员也不能使用泛型(这个和泛型类规定一样)
U u; // 在接口中无法使用会报错 -
泛型接口的类型,在继承接口或者实现接口时确定
-
没有指定类型,默认为
Object
八、自定义泛型方法
// 普通类中定义
public class Test {
// 泛型方法
public <T,R> void fly(T t,R r){
System.out.println("t : "+t);
System.out.println("r : "+r);
}
}
// 泛型类中定义
public class Fish<T,R> {
public void run(){};
public <U,M> void eat(U u,M m){ //泛型方法
}
}
// 调用
Test test = new Test();
test.fly("你好",123);
自定义泛型使用细节:
- 泛型方法,可以定义在普通类中,也可以定义在泛型类中
- 当泛型方法被调用时,类型会确定
public void eat(E e){}, 修饰符后没有<T,R...> eat方法不是泛型方法,而是使用了泛型public <E> void eat(E e){}正确使用
- 在泛型类中定义的时候,标识符最好和泛型类的标识符区分
九、泛型的继承和通配
-
泛型不具备继承性
// 虽然String继承自Object 但是这里会直接报错 ArrayList<Object> strings = new ArrayList<String>(); -
<?>:支持任意泛型类型 -
<? extends A>:支持A类以及A类的子类,规定了泛型的上限 -
<? super A>:支持A类以及A类的父类,不限于直接父类,规定了泛型的下限举例:
class AA{} class BB extends AA{} class CC extends BB{}import java.security.PublicKey; import java.util.ArrayList; import java.util.List; public class GenericTest { public static void main(String[] args) { ArrayList<Object> list1 = new ArrayList<>(); ArrayList<String> list2 = new ArrayList<>(); ArrayList<AA> list3 = new ArrayList<>(); ArrayList<BB> list4 = new ArrayList<>(); ArrayList<CC> list5 = new ArrayList<>(); // 如果是 List<?> c 就可以接收任意类型的List printCollection1(list1); printCollection1(list2); printCollection1(list3); printCollection1(list4); printCollection1(list5); // List<? extends AA> c // printCollection2(list1); // 传入不了 // printCollection2(list2); // 传入不了 // printCollection2(list3); // 传入成功 // printCollection2(list4); // 传入成功 // printCollection2(list5); // 传入成功 // List<? super AA> c // printCollection3(list1); // 传入成功 // printCollection3(list2); // 传入不了 // printCollection3(list3); // 传入成功 // printCollection3(list4); // 传入不了 // printCollection3(list5); // 传入不了 } public static void printCollection1(List<?> c){ for (Object o : c) { System.out.println(o); } } // ? extends AA 表示 上限 , 可以接收 AA 或者 AA 子类 public static void printCollection2(List<? extends AA> c){ for (Object aa : c) { System.out.println(aa); } } // ? super 子类类名 AA : 支持AA类以及 AA 类的父类,不限于直接父类 // 规定泛型的下限 public static void printCollection3(List<? super AA> c){ for (Object o : c) { System.out.println(o); } } }