1、泛型的理解和好处
1.1 使用传统方法的问题分析
- 无法对加入到几个ArrayList中的数据类型进行约束(不安全) 抛出ClassCastException异常
- 遍历的时候需要进行类型转化(向下转型),如果集合中的数据量较大,对效率有影响
1.2使用泛型
- 编译时,添加检查元素的类型,提高了安全性
- 减少了类型转化的次数
1.3小结(重点)
不使用泛型 Dog -加入-> Object -取出-> Dog 放入ArrayList中会先转成Object,取出再转成Dog
使用泛型 Dog --> Dog --> Dog
1.4 泛型理解 E
-
泛型是< E >
-
Dog,String这些是传入泛型的参数
1.5 泛型使用
1. E 表示 s的数据类型,该数据类型在定义Person对象的时候指定,即在编译期间,就确定了E是什么类型
Person<E>{
E s;//E 表示 s的数据类型,该数据类型在定义Person对象的时候指定,即在编译期间,就确定了E是什么类型
public Person(E s){
this.s = s;
}
public E f(){
return s;
}
}
2. 泛型的语法和使用
- List< Integer> list = new ArrayList< Integer>() OK
- List< int> list = new ArrayList< int>() 错误
泛型(T,E)只能是引用类型
泛型中可以传入子类
3. 在开发时往往进行简写(重点,推荐)
编译器会进行类型推断,所以后面的可以省略
List< Integer> list = new List<>();
1.6 泛型的遍历(迭代器 和 增强for循环)
1.7 泛型使用并且sort排序的例子
import java.util.ArrayList;
import java.util.Comparator;
public class Practice0559 {
public static void main(String[] args) {
ArrayList<Empolyee> list = new ArrayList<>();
list.add(new Empolyee("c",12,new MyDate(2000,11,12)));
list.add(new Empolyee("a",120,new MyDate(20000,110,120)));
list.add(new Empolyee("a",1200,new MyDate(200000,1100,1200)));
for (Empolyee empolyee : list) {
System.out.println(empolyee);
}
list.sort(new Comparator<Empolyee>() {
@Override
public int compare(Empolyee emp1, Empolyee emp2) {
//先按姓名排序,再按生日排序
//先对传入的参数进行验证
if(!(emp1 instanceof Empolyee && emp2 instanceof Empolyee)){
System.out.println("类型不匹配!");
return 0;
}
int i = emp1.getName().compareTo(emp2.getName());
if(i!=0) return i;
// int yearMinus = emp1.getBirthday().getYear()-emp2.getBirthday().getYear();
// if(yearMinus!=0) return yearMinus;
//
// int monthMinus = emp1.getBirthday().getMonth()-emp2.getBirthday().getMonth();
// if(monthMinus!=0) return monthMinus;
//
// return emp1.getBirthday().getDay()-emp2.getBirthday().getDay();
//封装到类中
return emp1.getBirthday().compareTo(emp2.getBirthday());
}
});
//排序后
System.out.println("排序后==================================");
for (Empolyee empolyee : list) {
System.out.println(empolyee);
}
}
}
class Empolyee{
private String name;
private double sal;
private MyDate birthday;
public Empolyee(String name, double 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 double getSal() {
return sal;
}
public void setSal(double sal) {
this.sal = sal;
}
public MyDate getBirthday() {
return birthday;
}
public void setBirthday(MyDate birthday) {
this.birthday = birthday;
}
@Override
public String toString() {
return "Empolyee{" +
"name='" + name + '\'' +
", sal=" + sal +
", birthday=" + birthday +
'}';
}
}
class MyDate implements Comparable<MyDate>{
private int month;
private int day;
private int year;
public MyDate(int month, int day, int year) {
this.month = month;
this.day = day;
this.year = year;
}
public int getMonth() {
return month;
}
public void setMonth(int month) {
this.month = month;
}
public int getDay() {
return day;
}
public void setDay(int day) {
this.day = day;
}
public int getYear() {
return year;
}
public void setYear(int year) {
this.year = year;
}
@Override
public String toString() {
return "MyDate{" +
"month=" + month +
", day=" + day +
", year=" + year +
'}';
}
@Override
public int compareTo(MyDate o) {//年月比较
int yearMinus = this.year-o.getYear();
if(yearMinus!=0) return yearMinus;
int monthMinus = this.month-o.getMonth();
if(monthMinus!=0) return monthMinus;
return this.day-o.getDay();
}
}
1.8 泛型接口的使用
- 无法初始化数组 因为数组在new时不能确定泛型的类型,就无法开辟内存空间,只能定义
- 静态方法无法使用泛型,同样原理,因为静态方法与对象无关,只与类有关,在类加载时对象还未创建
1.9 自定义泛型方法的使用(难点,重要)
- 泛型方法,可以定义在普通类中,也可以定义在泛型类中
- 修饰符 < T,R> 返回类型 方法名(T t,R r)(参数列表){}
- 自动识别泛型,并且装箱
- 如果修饰符后面没有加如泛型,方法就不是泛型方法,而是使用了泛型方法(前面定义的)(重要)
//泛型方法
public <T,R> void fly(T t,R r){
sout t.getclass();
sout r.getclass();
}
//使用
a.fly("string", 10);
//输出 String Integer
b.fly(10,10.1);
//输出 Integer Float
下面的例子中,T是类的泛型,而K是方法自己创建的泛型
public <K> void hello(T t,K k){};
1.10 泛型的继承和通配符
- 泛型不具备继承性
- <?> 支持任意泛型类型
List<Object> list = new ArryList<String> list; //错误
- <? extends A> 支持A类以及A类的子类,规定了泛型的上限
- <? super A> 支持A类以及A类的父类,规定了泛型的下限
1.11 JUnit使用 一般使用JUnit5.4
练习
import org.junit.jupiter.api.DynamicTest;
import org.junit.jupiter.api.Test;
import java.util.*;
public class Junit0565 {
public static void main(String[] args) {
}
@Test
public void testList(){
DAO<User> userDAO = new DAO<>();
userDAO.save("001",new User(1,10,"jack"));
userDAO.save("002",new User(2,20,"King"));
List<User> list = userDAO.list();
for (User user : list) {
System.out.println(user);
}
}
}
//定义User类
class User{
private int id;
private int age;
private String name;
public User(int id, int age, String name) {
this.id = id;
this.age = age;
this.name = name;
}
@Override
public String toString() {
return "User{" +
"id=" + id +
", age=" + age +
", name='" + name + '\'' +
'}';
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
//定义泛型类
class DAO<T>{
private Map<String, T> map = new HashMap<String,T>();
public DAO() {
}
public void save(String id, T entity){
map.put(id,entity);
}
public T get(String id){
return map.get(id);
}
public void update(String id,T entity){
map.put(id,entity);
}
//返回map中的所有T对象
public List<T> list(){
List<T> list = new ArrayList<>();
Set<String> keySet = map.keySet();
for (String key :keySet) {
list.add(map.get(key));
}
return list;
}
public void delete(String id){
map.remove(id);
}
public DAO(Map<String, T> map) {
this.map = map;
}
public Map<String, T> getMap() {
return map;
}
public void setMap(Map<String, T> map) {
this.map = map;
}
@Override
public String toString() {
return "DAO{" +
"map=" + map +
'}';
}
}
2、总结
1. 为什么需要泛型
- 不需要向下转换
- 减少安全隐患
2. 泛型介绍
- 语法
- 实例化
- 使用例子
3. 泛型使用
- 自定义泛型类
- 自定义泛型接口
- 自定义泛型方法 重点
- 泛型的继承和通配符
4. JUnit练习
5. 个人体会
更像是一种编码思路,能够灵活使用即可!