Java泛型详解
1. 泛型的理解与好处
1.1 泛型的引出
- 请编写程序,在ArrayList中,添加3个Dog对象
- Dog对象中含有name和age,并输出name和age(要求使用getXXX())
- 先使用传统的方法来解决
public class Generic01 {
public static void main(String[] args) {
ArrayList arrayList = new ArrayList();
arrayList.add(new Dog("皮皮", 5));
arrayList.add(new Dog("小黄", 6));
arrayList.add(new Dog("小虎", 8));
//遍历
//增强for
for (Object o : arrayList) {
Dog dog = (Dog) o;
System.out.println(dog.getName() + "_" + dog.getAge());
//皮皮_5
//小黄_6
//小虎_8
}
}
}
/*
1. 请编写程序,在ArrayList中,添加3个Dog对象
2. Dog对象中含有name和age,并输出name和age(要求使用getXXX())
*/
class Dog {
private String name;
private int age;
public Dog(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "Dog{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
- 上面的程序用传统的方法解决毫无问题,但是如果我们程序员在编写程序的时候不小心添加了一只猫,
- 那么我们在编译的时候不会报错,但是程序运行的就会出现类型转换异常
arrayList.add(new Cat("小虎", 8));//ClassCastException
- 使用传统的方法会存在以下问题:
- 不能对加入的集合ArrayList中的数据类型进行约束(不安全)
- 遍历的时候,需要进行类型转换,如果集合中的数据量较大,对效率有影响
- 那么我们就可以用泛型来解决以上的问题
public class Generic02 {
public static void main(String[] args) {
//代码解读
//1.当我们使用泛型 ArrayList<Dog1> 表示存放到 ArrayList 集合中的元素是 Dog1 类型
//2.如果编译器发现添加的类型不满足要求,即不是 Dog1 类型,就会报错
//3.在遍历的时候,可以直接取出 Dog1 类型,而不是 Object 类型
// 不用再进行向下转型
//4.public class ArrayList<E>{}
ArrayList<Dog1> arrayList = new ArrayList<Dog1>();
arrayList.add(new Dog1("皮皮", 5));
arrayList.add(new Dog1("小黄", 6));
arrayList.add(new Dog1("小虎", 8));
for (Dog1 dog : arrayList) {
System.out.println(dog.getName() + "_" + dog.getAge());
//皮皮_5
//小黄_6
//小虎_8
}
}
}
class Dog1 {
private String name;
private int age;
public Dog1(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "Dog{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
1.2 泛型的好处
-
编译时,检查添加元素的类型,提高了安全性
-
减少了类型转换的次数,提高了效率
-
不再提示编译警告
-
不使用泛型的时候:
- Dog -加入-> Object -取出-> Dog
- 即放入到ArrayList中会先转成Object,再取出的时候,还需要转换成Dog
-
使用泛型的时候:
- Dog -> Dog -> Dog
- 即放入和取出的时候,不需要进行类型转换,提高效率
1.3 泛型介绍
泛(广泛)型(类型)=>Integer,String,Dog
- 泛型又称参数化类型,是Jdk5.0出现的新特性,解决数据类型的安全性问题
- 在类声明或实例化时只要指定好需要的具体的类型即可。
- Java泛型可以保证如果程序在编译时没有发出警告,运行时就不会产生ClassCastException异常。同时,代码更加简洁、健壮
- 泛型的作用是:可以在类声明时通过一个标识表示类中某个属性的类型,或者是某个方法的返回值的类型,或者是参数类型。[有点难,下面用代码说明]
public class Generic03 {
public static void main(String[] args) {
//特别强调: E 具体的数据类型在定义 Person 对象的时候指定
// 即在编译期间,就确定 E 是什么类型
Person<String> person1 = new Person<String>("java");
person1.show();//class java.lang.String
/*可以理解为以下代码
class Person{
String s;
//E表示 s 的数据类型,该数据在定义Person对象的时候指定
//即在编译期间,就确定E是什么类型
//E也可以是参数类型
public Person(String s) {
this.s = s;
}
//返回值类型使用E
public String f(){
return s;
}
}
*/
Person<Integer> person2 = new Person<Integer>(168);
person2.show();//class java.lang.Integer
/*可以理解为以下代码
class Person{
Integer s;
//E表示 s 的数据类型,该数据在定义Person对象的时候指定
//即在编译期间,就确定E是什么类型
//E也可以是参数类型
public Person(Integer s) {
this.s = s;
}
//返回值类型使用E
public Integer f(){
return s;
}
}
*/
}
}
//泛型的作用是:可以在类声明时通过一个标识表示类中某个属性的类型,
// 或者是某个方法的返回值的类型,或者是参数类型。
class Person<E> {
E s;
//E表示 s 的数据类型,该数据在定义Person对象的时候指定
//即在编译期间,就确定E是什么类型
//E也可以是参数类型
public Person(E s) {
this.s = s;
}
//返回值类型使用E
public E f() {
return s;
}
//创建一个方法show输出显示s的运行类型
public void show() {
System.out.println(s.getClass());
}
}
2. 泛型的基础语法和使用
2.1 泛型的声明
- interface接口{}
- class类<K,V>{}
- 说明:
- 其中T,K,V不代表值,而是表示类型
- 任意的字母都可以,但是我们常用T,K,V表示
- 其中T是Type的缩写,K是Key的缩写,V是Value的缩写
2.2 泛型的实例化
- 要在类名后面指定类型参数的值(类型)。
- 例如
- List strList = new ArrayList;
- Iterator iterator = customer.iterator;
2.3 泛型使用举例
举例说明,泛型在HashSet,HashMap的使用情况
练习:
- 创建3个学生对象
- 放入到HashSet中学生对象使用
- 放入到HashMap中,要求Key是String name,Value就是学生对象
- 使用两种方式遍历
- 代码如下
public class GenericExercise {
public static void main(String[] args) {
//使用泛型方式给 HashSet 放入 3 个学生对象
Set<Student> students = new HashSet<Student>();
students.add(new Student("张飞", 18));
students.add(new Student("关羽", 19));
students.add(new Student("吕布", 17));
//遍历
for (Student student : students) {
System.out.println(student);
//Student{name='吕布', age=17}
//Student{name='关羽', age=19}
//Student{name='张飞', age=18}
}
//使用泛型方式给 HashMap 放入 3 个学生对象
//K -> String V->Student
Map<String, Student> hashMap = new HashMap<String, Student>();
//public class HashMap<K,V> {}
hashMap.put("张三", new Student("张三", 20));
hashMap.put("李四", new Student("李四", 22));
hashMap.put("王五", new Student("王五", 21));
//迭代器 EntrySet
/*
public Set<Map.Entry<K,V>> entrySet() { Set<Map.Entry<K,V>> es;
return (es = entrySet) == null ? (entrySet = new EntrySet()) : es;
}
*/
Set<Map.Entry<String, Student>> entries = hashMap.entrySet();
/*
public final Iterator<Map.Entry<K,V>> iterator() { return new EntryIterator();
}
*/
Iterator<Map.Entry<String, Student>> iterator = entries.iterator();
while (iterator.hasNext()) {
Map.Entry<String, Student> next = iterator.next();
System.out.println(next.getKey() + "_" + next.getValue());
//李四_Student{name='李四', age=22}
//张三_Student{name='张三', age=20}
//王五_Student{name='王五', age=21}
}
}
}
/*
1. 创建3个学生对象
2. 放入到HashSet中学生对象使用
3. 放入到HashMap中,要求Key是String name,Value就是学生对象
4. 使用两种方式遍历
*/
class Student {
private String name;
private int age;
public Student(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
2.4 泛型使用的注意事项和细节
-
interface List{} , public class HashSet{}…等等
- 说明:T、E在使用的时候只能是引用类型
-
看看下面语句是否正确?
//给泛型指定具体类型只能是引用类型,不能是基本类型 List<Integer> list = new ArrayList<Integer>();//ok List<int> list2 = new ArrayList<int>()://错误
-
在给泛型指定具体类型后,可以传入该类型或者其子类类型
-
泛型使用形式
List<Integer> list1 = new ArrayList<Integer>(); List<Integer> list2 = new ArrayList<>();
-
如果我们没有给泛型指定具体的类型,那么泛型默认是 Object 类型
-
下面简单的用代码演示
public class GenericDetail {
public static void main(String[] args) {
//2. 给泛型指定具体类型只能是引用类型,不能是基本类型
List<Integer> list = new ArrayList<Integer>();//ok
//List<int> list2=new ArrayList<int>()://错误
//3. 在给泛型指定具体类型后,可以传入该类型或者其子类类型
Cat<A> aCat = new Cat<A>(new A());
aCat.show(new A());//class com.song.generic.A
Cat<A> bCat = new Cat<A>(new B());
bCat.show(new B());//class com.song.generic.B
//4. 泛型使用形式
ArrayList<Integer> list1 = new ArrayList<Integer>();
List<Integer> list2 = new ArrayList<Integer>();
//在实际开发中,我们往往会简写
//编译器会自己进行推断
ArrayList<Integer> list3 = new ArrayList<>();
List<Integer> list4 = new ArrayList<>();
//5. 如果我们没有给泛型指定具体的类型,那么泛型默认是 Object 类型
ArrayList arrayList = new ArrayList();
//等价于 ArrayList<Object> arrayList = new ArrayList<>();
Pig pig = new Pig();
/*等价于
class Pig {
Object e;
public Pig() {
}
public Pig(Object e) {
this.e = e;
}
}
*/
}
}
class Pig<E> {
E e;
public Pig() {
}
public Pig(E e) {
this.e = e;
}
}
class A {
}
class B extends A {
}
class Cat<E> {
E e;
public Cat(E e) {
this.e = e;
}
public void show(E e) {
//打印运行类型
System.out.println(e.getClass());
}
}
2.5 泛型的练习题
定义Employee类
-
该类包含:private成员变量name , sal , birthday,其中 birthday为MyDate类的对象;
-
为每一个属性定义 getter setter 方法;
-
重写 toString 方法输出name , sal , birthday
-
MyDate类包含:private成员变量month , day , year;并为每一个属性定义getter setter 方法;
-
创建该类的3个对象,并把这些对象放入ArrayList集合中(ArrayList需使用泛型来定义),对集合中的元素进行排序,并遍历输出:
-
排序方式:调用ArrayList的sort 方法,传入Comparator对象[使用泛型],
-
先按照 name排序,如果name相同,则按生日日期的先后排序。【即:定制排序】
-
代码如下:
- GenericExercise02类的编写
public class GenericExercise02 { public static void main(String[] args) { ArrayList<Employee> employees = new ArrayList<>(); employees.add(new Employee("jack", 20000, new MyDate(1995, 12, 25))); employees.add(new Employee("jack", 18000, new MyDate(1993, 11, 26))); employees.add(new Employee("john", 18000, new MyDate(1996, 12, 26))); System.out.println("employees =" + employees); //employees =[ //Employee{name='jack', sal=20000.0, birthday=MyDate{year=1995, month=12, day=25}}, //Employee{name='jack', sal=18000.0, birthday=MyDate{year=1993, month=11, day=26}}, //Employee{name='john', sal=18000.0, birthday=MyDate{year=1996, month=12, day=26}}] System.out.println("====对雇员进行排序===="); employees.sort(new Comparator<Employee>() { @Override public int compare(Employee em1, Employee em2) { //先按照 name排序,如果name相同,则按生日日期的先后排序。【即:定制排序】 //1. 先对传入的参数进行验证 if (!(em1 instanceof Employee && em2 instanceof Employee)) { System.out.println("类型不正确"); return 0; } //2. 比较 name int i = em1.getName().compareTo(em2.getName()); if (i != 0) { return i; } //下面是对 birthday 的比较,因此,我们最好把这个比较,放在 MyDate 类完成 //封装后,将来可维护性和复用性,就大大增强. return em1.getBirthday().compareTo(em2.getBirthday()); /*把这段代码放入 MyDate中 //3. 如果name相同,就先比较 birthday - year int yearMinus = em1.getBirthday().getYear() - em2.getBirthday().getYear(); if (yearMinus != 0) { return yearMinus; } //4. 如果 year 相同,就比较 month int monthMinus = em1.getBirthday().getMonth() - em2.getBirthday().getMonth(); if (monthMinus != 0) { return monthMinus; } //4. 如果 year 和 month 相同,就比较 day return em1.getBirthday().getDay() - em2.getBirthday().getDay(); */ } }); System.out.println("====排序后===="); System.out.println(employees); //[ //Employee{name='jack', sal=18000.0, birthday=MyDate{year=1993, month=11, day=26}}, //Employee{name='jack', sal=20000.0, birthday=MyDate{year=1995, month=12, day=25}}, //Employee{name='john', sal=18000.0, birthday=MyDate{year=1996, month=12, day=26}}] } }
- Employee类的编写
public class Employee { /* 定义Employee类 1. 该类包含:private成员变量name , sal , birthday,其中 birthday为MyDate类的对象; 2) 为每一个属性定义 getter setter 方法; 3) 重写 toString 方法输出name , sal , birthday */ private String name; private double sal; private MyDate birthday; public Employee(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 "\nEmployee{" + "name='" + name + '\'' + ", sal=" + sal + ", birthday=" + birthday + '}'; } }
- MyDate类的编写
public class MyDate implements Comparable<MyDate> { /* MyDate类包含:private成员变量month , day , year;并为每一个属性定义getter setter 方法; */ private int year; private int month; private int day; public MyDate(int year, int month, int day) { this.year = year; this.month = month; this.day = day; } public int getYear() { return year; } public void setYear(int year) { 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; } @Override public String toString() { return "MyDate{" + "year=" + year + ", month=" + month + ", day=" + day + '}'; } @Override public int compareTo(MyDate o) { //3. 如果name相同,就先比较 birthday - year int yearMinus = this.year - o.getYear(); if (yearMinus != 0) { return yearMinus; } //4. 如果 year 相同,就比较 month int monthMinus = this.month - o.getMonth(); if (monthMinus != 0) { return monthMinus; } //4. 如果 year 和 month 相同,就比较 day return day - o.getDay(); } }
3. 自定义泛型
3.1 自定义泛型基本语法和注意细节
- 自定义泛型基本语法
class 类名<T,R...>{//…表示可以有多个泛型
成员
}
-
自定义语法注意细节
- 普通成员可以使用泛型(属性、方法)
- 使用泛型的数组,不能初始化
- 静态方法中不能使用类的泛型
- 泛型类的类型,是在创建对象时确定的(因为创建对象时,需要指定确定类型)
- 如果在创建对象时,没有指定类型,默认为Object
-
应用案例
- 阅读以下代码
class Tiger<T,R,M>{
String name;
T t;
R r;
M m;
}
- 代码解读:
public class CustomGenerics_ {
public static void main(String[] args) {
}
}
//代码解读
//1. Tiger后面有泛型,所以我们把 Tiger 称为自定义泛型
//2. T,R,M 泛型的标识符,一般是单个大写字母
//3. 泛型的标识符可以有多个
//4. 普通成员可以使用泛型(属性、方法)
//5. 使用泛型的数组,不能初始化
//6. 静态方法中不能使用类的泛型
class Tiger<T, R, M> {
String name;
T t;//属性使用泛型
R r;
M m;
//因为数组在new的时候,不能确定T的类型,就无法在内存开空间
//T[] ts = new T[8];//不能初始化
public Tiger(String name, T t, R r, M m) {//构造器使用泛型
this.name = name;
this.t = t;
this.r = r;
this.m = m;
}
//因为静态是和类相关的,在类加载的时候,对象还没创建
//所以如果静态方法和静态属性使用了泛型,JVM就无法完成初始化
/*不允许使用
static R r2;
public static void show(M m){}
*/
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public T getT() {//方法使用泛型
return t;//返回类型使用泛型
}
public void setT(T t) {
this.t = t;
}
public R getR() {
return r;
}
public void setR(R r) {
this.r = r;
}
public M getM() {
return m;
}
public void setM(M m) {
this.m = m;
}
}
- 判断以下代码是否正确
//class Tiger<T, R, M> {}
Tiger<Double,String,Integer> g = new Tiger<>("john");
//其中 T = Double,R = String,M = Integer;
g.setT(10.9); //OK
g.setT("yy"); //错误,类型不对
//因为"yy"为String类型,T为Double类型
System.out.println(g);
Tiger g2 = new Tiger("john~~");//OK
//g2的泛型的类型都是Object
g2.setT("yy"); //OK
//因为g2泛型类型都是Object,"yy"是String类型
//String类型是Object类型的子类
System.out.println("g2=" + g2);
3.2 自定义泛型接口
- 基础语法
interface 接口名<T,R...>{}
-
注意细节
- 接口中,静态成员也不能使用泛型(这个和泛型类规定一样)
- 泛型接口的类型,在继承接口或者实现接口时确定
- 没有指定类型,默认为Object
-
应用实例
下面举例说明自定义泛型接口的使用
public class CustomInterfaceGeneric { public static void main(String[] args) { } } /* 泛型接口使用的说明 1. 静态成员也不能使用泛型 2. 泛型接口的类型,在继承接口或者实现接口时确定 3. 没有指定类型,默认为Object */ interface IUsb<U, R> { int n = 10; //1. 静态成员也不能使用泛型 //U name;//不能这样使用 //普通方法中,可以使用接口泛型 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; } } //2. 泛型接口的类型,在继承接口或者实现接口时确定 //在继承接口 指定泛型接口的类型 interface IA extends IUsb<String, Double> { } //因为接口 IA 继承了 接口 IUSB, //所以AA类必须要重写所有接口IUSB里面的方法 //又因为接口IA继承接口IUSB的时候指定了泛型 //所以指定了U为String R为Double //所以当我们重写接口IUSB里面方法的时候 //使用String替换U,使用Double替换R class AA implements IA { @Override public Double get(String s) { return null; } @Override public void hi(Double aDouble) { } @Override public void run(Double r1, Double r2, String u1, String u2) { } } //实现接口时,直接指定泛型接口的类型 //给U指定了Integer,给R指定了Float //所以当我们实现IUSB方法时 //使用 Integer 替换 U ,使用 Float 替换 R class BB implements IUsb<Integer, Float> { @Override public Float get(Integer integer) { return null; } @Override public void hi(Float aFloat) { } @Override public void run(Float r1, Float r2, Integer u1, Integer u2) { } } //3. 没有指定类型,默认为Object //没有指定类型,默认为Object //建议直接写成 class CC implements IUsb<Object,Object>{ class CC implements IUsb {//等价于class CC implements IUsb<Object,Object>{ @Override public Object get(Object o) { return null; } @Override public void hi(Object o) { } @Override public void run(Object r1, Object r2, Object u1, Object u2) { } }
3.3 自定义泛型方法
- 基础语法
修饰符<T,R..>返回类型 方法名(参数列表){}
-
注意细节
- 泛型方法,可以定义在普通类中,也可以定义在泛型类中
- 当泛型方法被调用时,类型会确定
- public void eat(E e){},修饰符后没有<TR…> eat方法不是泛型方法,而是使用了泛型
- 泛型方法可以使用类声明的泛型,也可以使用自己声明的泛型
- 代码简单说明:
public class CustomMethodGeneric { public static void main(String[] args) { //2. 当泛型方法被调用时,类型会确定 Car car = new Car(); car.fiy("保时捷", 1000);//当调用方法时,传入参数,编译器就会确定类型 //class java.lang.String //class java.lang.Integer System.out.println("====================="); car.fiy(168, 12.8);//当调用方法时,传入参数,编译器就会确定类型 //class java.lang.Integer //class java.lang.Double //测试 Fish<String, ArrayList> fish = new Fish<>(); fish.tell(12.2, new ArrayList()); //class java.lang.Double //class java.util.ArrayList } } /* 泛型方法的使用: 1. 泛型方法,可以定义在普通类中,也可以定义在泛型类中 2. 当泛型方法被调用时,类型会确定 3. 修饰符后没有<TR..> 的方法不是泛型方法,而是使用了泛型 4. 泛型方法可以使用类声明的泛型,也可以使用自己声明的泛型 */ //1. 泛型方法,可以定义在普通类中,也可以定义在泛型类中 class Car { public void run() {//普通方法 } //1. <T,R>就是泛型 //2. 提供给fly使用的 public <T, R> void fiy(T t, R r) {//泛型方法 //这里会自动装箱 System.out.println(t.getClass());//String System.out.println(r.getClass());//Integer } } class Fish<T, R> {//泛型类 public void run() {//普通方法 } public <U, M> void eat(U u, M m) {//泛型方法 } //3. 修饰符后没有<TR..> 的方法不是泛型方法,而是使用了泛型 //说明 //1. 下面的say方法不是泛型方法 //2. 而是say方法使用了类声明的泛型 public void say(T t) { } //4. 泛型方法可以使用类声明的泛型,也可以使用自己声明的泛型 public <K> void tell(K k, R r) { System.out.println(k.getClass()); System.out.println(r.getClass()); } }
-
引用案例
下面代码是否正确,如果有错误,修改正确,并说明输出了什么?
class Apple<T,R,M>{ public <E> void fly(E e){ System.out.println(e.getClass().getSimpleName()); } public void eat(U u){}//错误,因为U没有声明 public void run(M m){}//OK } class Dog{} //下面代码输出了什么? Apple<String, Integer, Double> apple = new Apple<>(); apple.fly(10);//自动装箱 Integer10,输出Integer apple.fly(new Dog());//Dog
4. 泛型的继承和通配符
4.1 泛型的继承和通配符说明
-
泛型不具备继承性
List<Object> list=new ArrayList<String>();// 对吗?答:这是不可以的,泛型没有继承性
- <?>:支持任意泛型类型
- <?extendsA>:支持A类以及A类的子类,规定了泛型的上限
- <?superA>:支持A类以及A类的父类,不限于直接父类,规定了泛型的下限
-
代码简单演示
public class GenericExtends {
public static void main(String[] args) {
Object o = new String();
//泛型没有继承性
//List<Object> list=new ArrayList<String>();
//举例说明下面三个方法的使用
List<Object> list1 = new ArrayList<>();
List<String> list2 = new ArrayList<>();
List<A> list3 = new ArrayList<>();
List<IB> list4 = new ArrayList<>();
List<IC> list5 = new ArrayList<>();
//如果是 List<?> c ,可以接受任意的泛型类型
printCollection1(list1);
printCollection1(list2);
printCollection1(list3);
printCollection1(list4);
printCollection1(list5);
//List<? extends AA> c: 表示 上限,可以接受 A 或者 A 子类
//printCollection2(list1);//×
//printCollection2(list2);//×
printCollection2(list3);//√
printCollection2(list4);//√
printCollection2(list5);//√
//List<? super A> c: 支持 A 类以及 A 类的父类,不限于直接父类
printCollection3(list1);//√
//printCollection3(list2);//×
printCollection3(list3);//√
//printCollection3(list4);//×
//printCollection3(list5);//×
}
// ? extends AA 表示 上限,可以接受 AA 或者 AA 子类
public static void printCollection2(List<? extends A> c) {
for (Object object : c) {
System.out.println(object);
}
}
//说明: List<?> 表示 任意的泛型类型都可以接受
public static void printCollection1(List<?> c) {
for (Object object : c) { // 通配符,取出时,就是 Object
System.out.println(object);
}
}
// ? super 子类类名 AA:支持 AA 类以及 AA 类的父类,不限于直接父类,
//规定了泛型的下限
public static void printCollection3(List<? super A> c) {
for (Object object : c) {
System.out.println(object);
}
}
}
class A {
}
class IB extends A {
}
class IC extends IB {
}
4.2 JUnit
4.2.1 为什么需要JUnit
- 一个类有很多功能代码需要测试,为了测试,就需要写入到main方法中
- 如果有很多个功能代码测试,就需要来回注销,切换很麻烦
- 如果可以直接运行一个方法,并且可以给出相关信息,就会方便很多
- 在java中就有一个JUnit测试框架
4.2.2 JUnit基本介绍
- JUnit是一个Java语言的单元测试框架
- 多数Java的开发环境都已经集成了JUnit作为单元测试的工具
- 下面进行JUnit代码测试的演示
public class JUnit_ {
public static void main(String[] args) {
//用传统的方法调用
//new JUnit_().m1();
//new JUnit_().m2();
}
//在你想要测试的方法上输出 @Test
//然后按 Alt + Enter 选择JUnit5版本
//接着等待导入完成就可以使用JUnit进行测试了
@Test
public void m1() {
System.out.println("m1方法被调用");
}
@Test
public void m2() {
System.out.println("m2方法被调用");
}
}
4.3 练习
-
定义个泛型类 DAO,在其中定义一个Map 成员变量,Map的键为 String类型,值为T类型
-
分别创建以下方法:
(1) public void save(String idTentity):保存T类型的对象到 Map 成员变量中(2) public T get(Stringid):从map 中获取id 对应的对象
(3) public void update(String idTentity):替换map 中key为id的内容,改为entity对象
(4) public List list():返回 map 中存放的所有T对象
(5) public void delete(String id):删除指定id 对象
-
定义一个 User 类:
该类包含:private成员变量(int类型)id,age; (String类型)name。
创建 DAO 类的对象,分别调用其 save、get、update、list、delete 方法来操作 User 对象。
使用 Junit 单元测试类进行测试。
public class Homework01 {
public static void main(String[] args) {
}
@Test
public void testList() {
DAO<User> userDAO = new DAO<>();
userDAO.save("1", new User(1, 22, "jack"));
userDAO.save("2", new User(2, 18, "lucy"));
userDAO.save("3", new User(3, 28, "duck"));
List<User> list = userDAO.list();
System.out.println("list =" + list);
//输出 list =[User{id=1, age=22, name='jack'},
// User{id=2, age=18, name='lucy'},
// User{id=3, age=28, name='duck'}]
userDAO.update("1", new User(1, 19, "lily"));//修改
userDAO.delete("2");//删除
System.out.println("===修改后===");
list = userDAO.list();//重新获取一下list
System.out.println("list =" + list);
//输出 list =[User{id=1, age=19, name='lily'},
// User{id=3, age=28, name='duck'}]
//单独获取一个
System.out.println("id = 1" + userDAO.get("1"));
//输出 id = 1User{id=1, age=19, name='lily'}
}
}
class DAO<T> {
private Map<String, T> map = new HashMap<>();
//保存T类型的对象到 Map 成员变量中
public void save(String id, T entity) {
map.put(id, entity);
}
//从map 中获取id 对应的对象
public T get(String id) {
return map.get(id);
}
//替换map 中key为id的内容,改为entity对象
public void update(String id, T entity) {
map.put(id, entity);
}
//返回 map 中存放的所有T对象
//遍历map[k,v],将map的所有value(T,entity)
//然后封装到ArrayList返回即可
public List<T> list() {
List<T> list = new ArrayList<>();
Set<String> strings = map.keySet();
for (String key : strings) {
list.add(get(key));
//直接调用了上面的get方法
//也可以写成 m
// ap.get(key)
}
return list;
}
//删除指定id 对象
public void delete(String id) {
map.remove(id);
}
}
/*
1. 定义个泛型类 DAO<T>,在其中定义一个Map 成员变量,Map的键为 String类型,值为T类型
2. 分别创建以下方法:
(1) public void save(String id,T entity):保存T类型的对象到 Map 成员变量中
(2) public T get(String id):从map 中获取id 对应的对象
(3) public void update(String id,T entity):替换map 中key为id的内容,改为entity对象
(4) public List<T> list():返回 map 中存放的所有T对象
(5) public void delete(String id):删除指定id 对象
3. 定义一个 User 类:
该类包含:private成员变量(int类型)id,age; (String类型)name。
创建 DAO 类的对象,分别调用其 save、get、update、list、delete 方法来操作 User 对象。
使用 Junit 单元测试类进行测试。
*/
//第一步: 先定义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;
}
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;
}
@Override
public String toString() {
return "User{" +
"id=" + id +
", age=" + age +
", name='" + name + '\'' +
'}';
}
}