一、泛型语法

1.1 泛型的理解和好处

  1. 看一个需求:

    1. 请编写一个程序,在 ArrayList 中,添加3个 Dog 对象
    2. Dog 对象含有 name 和 age,并输出 name 和 age(要求使用 getXxx())
  2. 先使用传统方式解决 -> 引出泛型 ```java package com.eduhsp.generic_;

import java.util.ArrayList;

@SuppressWarnings({“all”}) public class Traditional_ { public static void main(String[] args) { ArrayList list = new ArrayList(); list.add(new Dog(“大黄”,10)); list.add(new Dog(“小黑”,5)); list.add(new Dog(“小花”,8)); //假如我们的程序员,不小心,添加了一只猫 list.add(new Cat(“小白”,3)); for (Object o : list) { System.out.println(“\n狗的名字: “ + ((Dog)o).getName()); System.out.println(“狗的年龄: “ + ((Dog)o).getAge()); } } } class Dog { private String name; private int age;

  1. public Dog(String name, int age) {
  2. this.name = name;
  3. this.age = age;
  4. }
  5. public String getName() {
  6. return name;
  7. }
  8. public void setName(String name) {
  9. this.name = name;
  10. }
  11. public int getAge() {
  12. return age;
  13. }
  14. public void setAge(int age) {
  15. this.age = age;
  16. }

} class Cat { private String name; private int age;

  1. public Cat(String name, int age) {
  2. this.name = name;
  3. this.age = age;
  4. }
  5. public String getName() {
  6. return name;
  7. }
  8. public void setName(String name) {
  9. this.name = name;
  10. }
  11. public int getAge() {
  12. return age;
  13. }
  14. public void setAge(int age) {
  15. this.age = age;
  16. }

}

  1. 3. **使用传统方法的问题分析**
  2. 1. 不能对加入到集合 ArrayList 中的数据类型进行约束(不安全)
  3. 1. 遍历的时候,需要进行类型转换,如果集合中的数据量较大,对效率有影响
  4. 4. **泛型快速体验:用泛型解决前面的问题**
  5. ```java
  6. package com.eduhsp.generic_;
  7. import java.util.ArrayList;
  8. /**
  9. * @author HarborGao
  10. * @version 1.0
  11. */
  12. @SuppressWarnings({"all"})
  13. public class Generic01 {
  14. public static void main(String[] args) {
  15. ArrayList<Dog> list = new ArrayList<Dog>();
  16. //解读:
  17. //1. 当加入了泛型约束,存放到集合中的元素被指定
  18. //2. 如果编译器发现添加的类型,不满足要求,就会报错
  19. //3. 在遍历的时候,可以直接取出 Dog 类型而不是 Object 类型
  20. list.add(new Dog("大黄",10));
  21. list.add(new Dog("小黑",5));
  22. list.add(new Dog("小花",8));
  23. //假如我们的程序员,不小心,添加了一只猫
  24. //list.add(new Cat("小白",3)); //会编译时报错
  25. for (Dog dog : list) { //可以直接获取 Dog类型
  26. System.out.println(dog.getName() + " - " + dog.getAge());
  27. }
  28. }
  29. }
  1. 泛型的好处

    1. 编译时,检查添加元素的类型,提高了安全性
    2. 减少了类型转换的次数,提高效率

      不使用泛型:Dog -> Object -> Dog //放入到 ArrayList 会先转成 Object,在取出时,还需要转成Dog 使用泛型:Dog -> Dog -> Dog //放入时,和取出时,不需要类型转换,提高效率

    3. 不再提示编译警告

1.2 泛型的简单介绍

  1. 泛型又称参数化类型,是 Jdk 5.0 出现的新特性,解决数据类型的安全性问题
  2. 在类声明或实例化时只要指定好需要的具体的类型即可
  3. Java 泛型可以保证如果程序员在编译时没有发出警告,运行时就不会产生ClassCastException异常。同时,代码更加简洁,健壮。
  4. 泛型的作用是:可以在类声明时通过一个标识表示类中某个属性的类型,或者是某个方法的返回值的类型,或者是参数类型 ```java package com.eduhsp.generic_;

/**

  • @author HarborGao
  • @version 1.0 */

/*

  • 泛型的作用是:可以在类声明时通过一个标识表示类中某个属性的类型,
  • 或者是某个方法的返回值的类型,或者是参数类型 */ public class Generic02 { public static void main(String[] args) {

    1. //注意,特别强调:E具体的数据类型在定义Person对象的时候指定,即在编译期间,就确定了E是什么类型
    2. Person<String> person = new Person<>("hello");
    3. System.out.println(person.f());
    4. person.t();
    5. Person<Integer> person1 = new Person<>(100);
    6. System.out.println(person1.f());
    7. person1.t();

    } } class Person { E s; //E表示s的数据类型,该数据类型是在定义Person对象的时候指定,即在编译期间,就确定E是什么类型

    public Person(E s) { //E也可以是参数类型

    1. this.s = s;

    } public E f() { //返回类型使用E

    1. return s;

    } public void t() {

    1. System.out.println(s.getClass());

    } } ```

1.3 泛型的声明

interface 接口{} 和 class 类{}
//比如:List,ArrayList
说明:

  1. 其中,T, K, V 不代表值,而是表示类型
  2. 任意字母都可以。常用T表示,是Type 的缩写

1.4 泛型的实例化

要在类名后面指定类型参数的值(类型) 如:

  1. List strList = new ArrayList();
  2. Iterator iterator = customers.iterator();

1.5 泛型语法和使用

  1. package com.eduhsp.generic_;
  2. import java.util.*;
  3. @SuppressWarnings({"all"})
  4. public class GenericExercise {
  5. public static void main(String[] args) {
  6. HashSet<Student> students = new HashSet<Student>();
  7. students.add(new Student("小明",10));
  8. students.add(new Student("小红",12));
  9. students.add(new Student("小强",15));
  10. for (Student student : students) {
  11. System.out.println(student);
  12. }
  13. HashMap<String, Student> hm = new HashMap<>();
  14. hm.put("小明", new Student("小明",10));
  15. hm.put("小红", new Student("小红",12));
  16. hm.put("小强", new Student("小强",15));
  17. Set<Map.Entry<String, Student>> entries = hm.entrySet();
  18. Iterator<Map.Entry<String, Student>> iterator = entries.iterator();
  19. while (iterator.hasNext()) {
  20. Map.Entry<String, Student> next = iterator.next();
  21. System.out.println(next.getKey() + " - " + next.getValue());
  22. }
  23. }
  24. }

1.6 泛型使用的注意事项和细节

  1. interface List{} , public class HashSet{} …等等

说明:T, E 只能是引用类型
看看下面语句是否正确?
List list = new ArrayList();
List list2 = new ArrayList();

  1. 在给泛型指定具体类型后,可以传入该类型或者其子类类型

  2. 泛型使用形式:

List list1 = new ArrayList();
List list2 = new ArrayList<>(); //在实际开发中,往往这样简写,编译器会自动进行类型推断
如果这样写:List list3 = new ArrayList(); 默认给它的泛型是 Object
等价于:List list3 = new ArrayList<>();

1.7 泛型小练习

定义Employee类

  1. 该类包含: private成员变量name,sal,birthday, 其中birthday为MyDate类的对象;
  2. 为每一个属性定义getter, setter 方法;
  3. 重写 toString 方法输出 name, sal, birthday
  4. MyDate类包含: private成员变量month,day,year;并为每一个属性定义getter, setter方法;
  5. 创建该类的3个对象,并把这些对象放入ArrayList集合中(ArrayList 需使用泛型来定义) ,对集合中的元素进行排序,并遍历输出

排序方式:调用 ArrayList 的sort方法,传入Comparator对象[使用泛型],先按照 name排序,如果name相同,则按生日日期的先后排序。【即: 定制排序】

  1. package com.eduhsp.generic_;
  2. import java.util.ArrayList;
  3. import java.util.Comparator;
  4. /**
  5. * @author HarborGao
  6. * @version 1.0
  7. */
  8. public class GenericExercise01 {
  9. public static void main(String[] args) {
  10. Employee zhang = new Employee("zhang", 1500, new MyDate(5, 16, 1996));
  11. Employee li = new Employee("li", 1800, new MyDate(6, 6, 1997));
  12. Employee li2 = new Employee("li", 1800, new MyDate(3, 30, 1998));
  13. Employee wang = new Employee("wang", 1300, new MyDate(1, 20, 1999));
  14. ArrayList<Employee> employees = new ArrayList<>();
  15. employees.add(zhang);
  16. employees.add(li2);
  17. employees.add(wang);
  18. employees.add(li);
  19. System.out.println(employees);
  20. employees.sort(new Comparator<Employee>() {
  21. @Override
  22. public int compare(Employee o1, Employee o2) {
  23. if (o1.getName().equals(o2.getName())) return o1.getBirthday().compareTo(o2.getBirthday());
  24. return o1.getName().compareTo(o2.getName());
  25. }
  26. });
  27. for (Employee employee : employees) {
  28. System.out.println(employee);
  29. }
  30. }
  31. }
  32. class Employee {
  33. private String name;
  34. private double sal;
  35. private MyDate birthday;
  36. public Employee(String name, double sal, MyDate birthday) {
  37. this.name = name;
  38. this.sal = sal;
  39. this.birthday = birthday;
  40. }
  41. public String getName() {
  42. return name;
  43. }
  44. public void setName(String name) {
  45. this.name = name;
  46. }
  47. public double getSal() {
  48. return sal;
  49. }
  50. public void setSal(double sal) {
  51. this.sal = sal;
  52. }
  53. public MyDate getBirthday() {
  54. return birthday;
  55. }
  56. public void setBirthday(MyDate birthday) {
  57. this.birthday = birthday;
  58. }
  59. @Override
  60. public String toString() {
  61. return "\nEmployee{" +
  62. "name='" + name + '\'' +
  63. ", sal=" + sal +
  64. ", birthday=" + birthday +
  65. '}';
  66. }
  67. }
  68. class MyDate implements Comparable<MyDate> {
  69. private int month;
  70. private int day;
  71. private int year;
  72. public MyDate(int month, int day, int year) {
  73. this.month = month;
  74. this.day = day;
  75. this.year = year;
  76. }
  77. public int getMonth() {
  78. return month;
  79. }
  80. public void setMonth(int month) {
  81. this.month = month;
  82. }
  83. public int getDay() {
  84. return day;
  85. }
  86. public void setDay(int day) {
  87. this.day = day;
  88. }
  89. public int getYear() {
  90. return year;
  91. }
  92. public void setYear(int year) {
  93. this.year = year;
  94. }
  95. @Override
  96. public String toString() {
  97. return year + "-" + month + "-" + day;
  98. }
  99. @Override
  100. public int compareTo(MyDate anotherMyDate) {
  101. if (this.year == anotherMyDate.year && this.month == anotherMyDate.month) {
  102. return compare(this.day, anotherMyDate.day);
  103. } else if (this.year == anotherMyDate.year) {
  104. return compare(this.month, anotherMyDate.month);
  105. }
  106. return compare(this.year, anotherMyDate.year);
  107. }
  108. public static int compare(int x, int y) {
  109. return (x < y) ? -1 : ((x == y) ? 0 : 1);
  110. }
  111. }

二、自定义泛型

2.1 基本语法

class 类名 {
成员
}

2.2 注意细节

  1. 普通成员可以使用泛型(属性、方法)
  2. 使用泛型的数组,不能初始化
  3. 静态方法中不能使用类的泛型
  4. 泛型类的类型,是在创建对象时确定的(因为创建对象时,需要指定确定类型)
  5. 如果在创建对象时,没有指定类型,默认为 Object ```java package com.eduhsp.generic_;

public class CustomGeneric_ { public static void main(String[] args) {

  1. }

} //解读 //1. Tiger 后面有泛型,所以我们把 Tiger 就成为为自定义泛型类 //2. T, R, M 泛型的标识符,一般是单个大写字母 //3. 泛型的标识符可以有多个 //4. 普通成员可以使用泛型(属性、方法) //5. 使用泛型的数组,不能初始化 //6. 静态成员不能使用类的泛型 class Tiger { String name; T t; //属性适用泛型 R r; M m; //static R r2; //因为数组在 new时 不能确定T的类型,就无法在内存开空间 //T[] ts = new T[8];

  1. public Tiger(String name, T t, R r, M m) { //构造器使用泛型
  2. this.name = name;
  3. this.t = t;
  4. this.r = r;
  5. this.m = m;
  6. }
  7. //因为静态是和类相关的,在类加载时,对象还没有创建
  8. //所以,如果静态方法或者静态属性使用了泛型,JVM就无法完成初始化
  9. public /* static */ void fl(M m) {
  10. }
  11. public String getName() {
  12. return name;
  13. }
  14. public void setName(String name) {
  15. this.name = name;
  16. }
  17. //方法使用泛型
  18. public T getT() {
  19. return t;
  20. }
  21. public void setT(T t) {
  22. this.t = t;
  23. }
  24. public R getR() {
  25. return r;
  26. }
  27. public void setR(R r) {
  28. this.r = r;
  29. }
  30. public M getM() {
  31. return m;
  32. }
  33. public void setM(M m) {
  34. this.m = m;
  35. }
  36. public void f() {
  37. }

}

  1. <a name="qOzhW"></a>
  2. #### 2.3 自定义泛型接口
  3. **基本语法**<br />interface 接口名<T, R ...> {<br />}
  4. **注意细节**
  5. 1. 接口中,静态成员不能使用泛型
  6. 1. 泛型接口的类型,在继承接口或者实现接口时确定
  7. 1. 没有确定的话,默认是 Object
  8. **应用案例**
  9. ```java
  10. package com.eduhsp.generic_;
  11. /**
  12. * @author HarborGao
  13. * @version 1.0
  14. */
  15. public class CustomInterfaceGeneric {
  16. }
  17. /**
  18. * 泛型接口使用的说明
  19. * 1. 接口中,静态成员不能使用泛型
  20. * 2. 泛型接口的类型,在继承接口或者实现接口时确定
  21. * 3. 没有确定的话,默认是 Object
  22. */
  23. interface IUsb<U, R> {
  24. //普通方法中,可以使用接口泛型
  25. R get(U u);
  26. //U u; //在接口中,所有成员都是静态的,所以这样写也是错误的
  27. void h1(R r);
  28. void run(R r1, R r2, U u1, U u2);
  29. //在 jdk8 中,可以在接口中,使用默认方法,也是可以使用泛型
  30. default R method(U u) {
  31. return null;
  32. }
  33. }
  34. //在继承接口,指定泛型接口的类型
  35. interface IA extends IUsb<String, Double> {
  36. }
  37. //当我们去实现 IA接口时,因为IA在继承IUsb接口时,指定了泛型U为String R为Double
  38. //所以在实现接口方法时,使用String替换U,使用Double替换 R
  39. class AA implements IA {
  40. @Override
  41. public Double get(String s) {
  42. return null;
  43. }
  44. @Override
  45. public void h1(Double aDouble) {
  46. }
  47. @Override
  48. public void run(Double r1, Double r2, String u1, String u2) {
  49. }
  50. }
  51. //在实现接口时,指定泛型接口的类型
  52. //给U指定Integer 给R指定Float
  53. class BB implements IUsb<Integer, Float> {
  54. @Override
  55. public Float get(Integer integer) {
  56. return null;
  57. }
  58. @Override
  59. public void h1(Float aFloat) {
  60. }
  61. @Override
  62. public void run(Float r1, Float r2, Integer u1, Integer u2) {
  63. }
  64. }
  65. //没有指定类型,默认是Object
  66. class CC implements IUsb {
  67. @Override
  68. public Object get(Object o) {
  69. return null;
  70. }
  71. @Override
  72. public void h1(Object o) {
  73. }
  74. @Override
  75. public void run(Object r1, Object r2, Object u1, Object u2) {
  76. }
  77. }

2.4 自定义泛型方法

基本语法
修饰符 返回类型 方法名(参数列表) {
}

注意细节

  1. 泛型方法,可以定义在普通类中,也可以定义在泛型类中
  2. 当泛型方法被调用时,类型会确定
  3. public void eat(E e) {} 修饰符后没有 则 eat 方法不是泛型方法,而是使用了泛型

应用案例

  1. package com.eduhsp.generic_;
  2. public class CustomMethodGeneric {
  3. public static void main(String[] args) {
  4. Car car = new Car();
  5. car.fly("宝马",100); //当调用方法时,传入参数,编译器,就会确定类型
  6. }
  7. }
  8. //泛型方法,可以定义在普通类中,也可以定义在泛型类中
  9. class Car {//普通类
  10. public void run() { //普通方法
  11. }
  12. //说明:
  13. //1. <T, R> 就是泛型
  14. //2. 是提供给 fly 使用的
  15. public <T, R> void fly(T t, R r) { //泛型方法
  16. }
  17. }
  18. class Fish<T, R> { //泛型类
  19. public <U, M> void fly(U u, M m) { //泛型方法
  20. }
  21. //说明
  22. //1. 下面 hi 方法不是泛型方法
  23. //2. 是 hi 方法使用了类声明的 泛型
  24. public void hi(T t) {
  25. }
  26. //泛型方法,可以使用类声明的泛型,也可以使用自己声明泛型
  27. public<G> void hello(R r,G g) {
  28. }
  29. }

三、泛型继承和通配符

3.1 泛型的继承和通配符说明

  1. 泛型不具备继承性
    List list = new ArrayList(); //错误
  2. <?>:支持任意泛型类型
  3. <? extends A>:支持A类以及A类的子类,规定了泛型的上限
  4. <? super A>:支持A类以及A类的父类,不限于直接父类,规定了泛型的下限
  5. 3.2 案例演示

    1. package com.eduhsp.generic_;
    2. import java.util.ArrayList;
    3. import java.util.List;
    4. public class GenericExtends {
    5. public static void main(String[] args) {
    6. //1. 泛型不具备继承性
    7. //List<Object> list = new ArrayList<String>(); //错误
    8. //举例说明下面三个方法的使用
    9. ArrayList<Object> list1 = new ArrayList<>();
    10. ArrayList<String> list2 = new ArrayList<>();
    11. ArrayList<Aa> list3 = new ArrayList<>();
    12. ArrayList<Bb> list4 = new ArrayList<>();
    13. ArrayList<Cc> list5 = new ArrayList<>();
    14. //List<?> c
    15. printCollection1(list1);
    16. printCollection1(list2);
    17. printCollection1(list3);
    18. printCollection1(list4);
    19. printCollection1(list5);
    20. //List<? extends Aa> c
    21. // printCollection2(list1); //x
    22. // printCollection2(list2); //x
    23. printCollection2(list3); //√
    24. printCollection2(list4); //√
    25. printCollection2(list5); //√
    26. //List<? super Aa> c
    27. printCollection3(list1); //√
    28. // printCollection3(list2); //×
    29. printCollection3(list3); //√
    30. // printCollection3(list4); //×
    31. // printCollection3(list5); //×
    32. }
    33. //2. <?>:支持任意泛型类型
    34. public static void printCollection1(List<?> c) {
    35. for (Object obj : c) { //通配符,取出时,就是Object
    36. System.out.println(obj);
    37. }
    38. }
    39. //3. <? extends A>:支持A类以及A类的子类,规定了泛型的上限
    40. public static void printCollection2(List<? extends Aa> c) {
    41. for (Object obj : c) { //通配符,取出时,就是Object
    42. System.out.println(obj);
    43. }
    44. }
    45. //4. <? super A>:支持A类以及A类的父类,不限于直接父类,规定了泛型的下限
    46. public static void printCollection3(List<? super Aa> c) {
    47. for (Object obj : c) { //通配符,取出时,就是Object
    48. System.out.println(obj);
    49. }
    50. }
    51. }
    52. class Aa {}
    53. class Bb extends Aa {}
    54. class Cc extends Bb {}

    四、JUnit

    4.1 为什么需要 JUnit

    1. 一个类有很多功能代码需要测试,为了测试,就需要写入到 main 方法中
    2. 如果有多个功能代码测试,就需要来回注销(给代码段加注释,防止影响测试其他代码),切换很麻烦
    3. 如果可以直接运行一个方法,就方便很多,并且可以给出相关信息,就好了

    4.2 基本介绍

    1. JUnit 是一个Java语言的单元测试框架
    2. 多数Java的开发环境都已经集成了 JUnit 作为单元测试的工具 ```java package com.eduhsp.generic.junit;

    import org.junit.jupiter.api.Test;

    public class JUnit01 { public static void main(String[] args) { //传统方式 // new JUnit01().m1(); // new JUnit01().m2(); }

    1. @Test
    2. public void m1() {
    3. System.out.println("m1方法被调用。。。");
    4. }
    5. @Test
    6. public void m2() {
    7. System.out.println("m2方法被调用。。。");
    8. }

    }

    1. <a name="RBhZX"></a>
    2. #### 4.3 第一次使用配置
    3. 1. 在方法上方 写 @Test 然后按 alt+enter 快捷键
    4. 1. 选择 add 'JUnit5.x.x' to classpath 然后下一步
    5. 1. 等待IDEA配置完成
    6. <a name="hgPFG"></a>
    7. ### 五、本章作业
    8. 1. 编程题
    9. 定义个泛型类DAO<T>,在其中定义个Map成员变量,Map的键为String类型,值为T类型。<br />分别创建以下方法:<br />(1) public void save(String id,T entity):保存 T类型的对象到Map成员变量中<br />(2) public T get(String id):从map中获取id对应的对象<br />(3) public void update(String id,T entity):替换map中key为id的内容,改为entity对象<br />(4) public List<T> list():返回map中存放的所有T对象<br />(5) public void delete(String id):删除指定id对象<br />定义一个User类:<br />该类包含: private 成员变量 (int类型) id, age; (String类型) name<br />创建DAO类的对象,分别调用其 save、get、 update、 list、 delete 方法来操作User对象,<br />使用Junit单元测试类进行测试。
    10. ```java
    11. package com.eduhsp.generic_.homework_.homework01;
    12. import org.junit.jupiter.api.Test;
    13. import java.util.*;
    14. /**
    15. * @author HarborGao
    16. * @version 1.0
    17. */
    18. public class Homework01 {
    19. public static void main(String[] args) {
    20. }
    21. @Test
    22. public void testList() {
    23. DAO<User> dao = new DAO<>();
    24. dao.save("1001",new User(1001, 18, "jack"));
    25. dao.save("1002",new User(1002,18,"king"));
    26. dao.save("1003",new User(1003,18,"tom"));
    27. List<User> list = dao.list();
    28. System.out.println("list=" + list);
    29. dao.update("1002",new User(1002,16,"smith"));
    30. dao.delete("1003");
    31. System.out.println(dao.get("1002"));
    32. list = dao.list();
    33. System.out.println("list=" + list);
    34. }
    35. }
    36. class DAO<T> {
    37. private Map<String, T> map = new HashMap<>(); //注意!!! 自己这里之前写错了
    38. public void save(String id, T entity) {
    39. map.put(id, entity);
    40. } //保存 T类型的对象到Map成员变量中
    41. public T get(String id) {
    42. return map.get(id);
    43. } //从map中获取id对应的对象
    44. public void update(String id, T entity) {
    45. map.put(id, entity);
    46. } //替换map中key为id的内容,改为entity对象
    47. public List<T> list() {
    48. Collection<T> values = map.values();
    49. List<T> ts = new ArrayList<>();
    50. for (T o : values) {
    51. ts.add(o);
    52. }
    53. return ts;
    54. } //返回map中存放的所有T对象
    55. public void delete(String id) {
    56. map.remove(id);
    57. } //删除指定id对象
    58. public Map<String, T> getMap() {
    59. return map;
    60. }
    61. public void setMap(Map<String, T> map) {
    62. this.map = map;
    63. }
    64. }
    65. class User {
    66. private int id;
    67. private int age;
    68. private String name;
    69. public User(int id, int age, String name) {
    70. this.id = id;
    71. this.age = age;
    72. this.name = name;
    73. }
    74. public int getId() {
    75. return id;
    76. }
    77. public void setId(int id) {
    78. this.id = id;
    79. }
    80. public int getAge() {
    81. return age;
    82. }
    83. public void setAge(int age) {
    84. this.age = age;
    85. }
    86. public String getName() {
    87. return name;
    88. }
    89. public void setName(String name) {
    90. this.name = name;
    91. }
    92. @Override
    93. public String toString() {
    94. return "User{" +
    95. "id=" + id +
    96. ", age=" + age +
    97. ", name='" + name + '\'' +
    98. '}';
    99. }
    100. }

    学习参考(致谢):

    1. B站 @程序员鱼皮 Java学习一条龙
    2. B站 @韩顺平 零基础30天学会Java