1. 泛型的理解和好处
-
看一个需求
-
请编写程序,在ArrayList中,添加3个Dog对象
-
Dog对象含有name和age,并输出name和age(要求使用getXxx())
传统方式
public class Generics_ { @SuppressWarnings({"all"}) public static void main(String[] args) { ArrayList arrayList = new ArrayList(); arrayList.add(new Dog("DaHuang",3)); arrayList.add(new Dog("XiaoHua",4)); arrayList.add(new Dog("TuanTuan",2)); for (int i = 0; i < arrayList.size(); i++) { Dog d = (Dog)arrayList.get(i); //假如程序员不小心添加了一只猫,会抛出ClassCastException异常 System.out.println(d.getName() + "-" + d.getAge()); } } } 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中的数据类型进行约束(不安全)
-
遍历的时候需要进行类型转换,如果集合中数据量较大,对效率有影响
泛型快速体验-用泛型解决前面的问题
package com.mdklea.generics; import java.util.ArrayList; public class Generics02 { public static void main(String[] args) { //解读 //1. 当我们 ArrayList<GDog> 表示存放到 ArrayList 集合中的元素是Dog类型 //2. 如果编译器发现添加的类型不满足要求就会报错 //3. 在遍历的时候可以直接取出GDog而不是Object ArrayList<GDog> dogs = new ArrayList<GDog>(); dogs.add(new GDog("DaHuang",2)); dogs.add(new GDog("XiaoHua",7)); dogs.add(new GDog("XiaoMei",5)); for (GDog dog :dogs) { System.out.println(dog.getName() + "-" + dog.getAge()); } } } class GDog { private String name; private int age; public GDog(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 "GDog{" + "name='" + name + '\'' + ", age='" + age + '\'' + '}'; } }
-
-
-
泛型的好处
-
编译时,检查添加元素的类型,提高了安全性
-
减少了类型转换的次数,提高效率
-
不再显示编译警告
-
2.泛型介绍
理解:泛(广泛)型(类型) => Integer,String,Dog
-
泛型又称为参数化类型,是JDK5.0出现的新特性,解决数据类型的安全性问题
-
在类声明或实例化时只要指定好需要的具体的类型即可
-
java泛型可以保证如果程序在编译时没有发出警告,运行就不会产生ClassCastException异常。同时,代码更加简洁、健壮
-
泛型的作用是:可以在类声明时通过一个标识符表示类中某个属性的类型,或者是某个方法返回值的类型,或者是参数类型。
-
ss可以理解为一种能够接收数据类型的数据类型
package com.mdklea.generics; public class Generics03 { public static void main(String[] args) { Person<String> stringPerson = new Person<String>("m1"); System.out.println(stringPerson.f()); } } class Person<E>{ E s; //E表示 s的数据类型,该数据类型在定义Person对象的时候指定,即在编译期间,就确定E是什么类型 public Person(E s) { //E也可以是参数类型 this.s = s; } public E f() { //E也可以是返回类型 return s; } }
-
特别强调:E具体的数值类型在定义Person对象的时候指定,即在编译期间,就确定E是什么类型
3.泛型的语法
-
泛型的声明
interface 接口<T>{} 和class 类<K,V>{}
//比如List,ArrayList
-
其中,K,T,V不代表值,而是表示类型。
-
任意字母都可以。常用T表示,是Type的缩写
-
-
泛型的实例化
要在类名后面指定类型参数的值(类型)。如:
-
List<String> strList = new ArrayList<String>();
-
Iterator<Customer> iterator = customers.iterator();
-
package com.mdklea.generics;
import java.util.*;
public class GenericExercise {
@SuppressWarnings({"all"})
public static void main(String[] args) {
HashMap<String, Student> studentHashMap = new HashMap<>();
HashSet<Student> studentHashSet = new HashSet<>();
Student m1 = new Student("马1", 700);
Student m2 = new Student("马2", 720);
Student xxp = new Student("xxp", 730);
studentHashMap.put(m1.getName(),m1);
studentHashMap.put(m2.getName(),m2);
studentHashMap.put(xxp.getName(),xxp);
Set<Map.Entry<String, Student>> entries = studentHashMap.entrySet();
for (Map.Entry entry :entries) {
System.out.println(entry.getKey() + "-" + entry.getValue());
}
Set<String> set = studentHashMap.keySet();
System.out.println("==============================");
for (String stu :set) {
System.out.println(stu + studentHashMap.get(stu));
}
System.out.println("======================================");
studentHashSet.add(new Student("XiaoWang",583));
studentHashSet.add(new Student("XiaoMei",432));
studentHashSet.add(new Student("XiaoYe",283));
for (Student stu :studentHashSet) {
System.out.println(stu);
}
System.out.println("itit");
Iterator<Student> iterator = studentHashSet.iterator();
while (iterator.hasNext()) {
Student next = iterator.next();
System.out.println(next);
}
}
}
class Student {
private String name;
private int grade;
public Student(String name, int grade) {
this.name = name;
this.grade = grade;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getGrade() {
return grade;
}
public void setGrade(int grade) {
this.grade = grade;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", grade=" + grade +
'}';
}
}
4.泛型使用的注意事项和细节
-
interface List <T>{},Public class HashSet<E> {}...等等
说明: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<> {};
如果我们这样写 List list3 = new ArrayList(); 默认给它的泛型就是[<E> E就是Object]
5.课堂练习
package com.mdklea.generics;
import java.util.ArrayList;
import java.util.Comparator;
public class GenericExercise01 {
@SuppressWarnings({"all"})
public static void main(String[] args) {
ArrayList<Employee> employees = new ArrayList<>();
employees.add(new Employee("mda",30000,new Employee().OMyDate(6,10,2000)));
employees.add(new Employee("mda",30000,new Employee().OMyDate(21,10,2000)));
employees.add(new Employee("mda",30000,new Employee().OMyDate(2,10,2000)));
employees.sort(new Comparator<Employee>() {
@Override
public int compare(Employee o1, Employee o2) {
//比较名字
int i = o1.getName().compareTo(o2.getName());
//可以不写那么多else
if (i != 0){
return i;
}else {
i = (int)(o1.getSal() - o2.getSal());
if (i != 0){
return i;
}else {
//把年月日的写法写到MyDate中
return o1.getBirthday().compareTo(o2.getBirthday());
}
}
}
});
for (Employee e :employees) {
System.out.println(e);
}
}
}
@SuppressWarnings({"all"})
class Employee {
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 Employee() {
}
//不写内部类貌似更方便
class MyDate implements Comparable<MyDate>{
private int day;
private int month;
private int year;
public MyDate(int day, int month, int year) {
this.day = day;
this.month = month;
this.year = year;
}
public int getDay() {
return day;
}
public void setDay(int day) {
this.day = day;
}
public int getMonth() {
return month;
}
public void setMonth(int month) {
this.month = month;
}
public int getYear() {
return year;
}
public void setYear(int year) {
this.year = year;
}
@Override
public String toString() {
return day +
"-" + month +
"-" + year ;
}
@Override
public int compareTo(MyDate o) {
int yearMinus = this.year - o.year;
if (yearMinus != 0){
return yearMinus;
}
int monthMinus = this.month - o.month;
if (monthMinus != 0){
return monthMinus;
}
int dayMinus = this.day - o.day;
return dayMinus;
}
}
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 "Employee{" +
"name='" + name + '\'' +
", sal=" + sal +
", birthday=" + birthday +
'}';
}
public MyDate OMyDate(int day,int month,int year){
return new MyDate(day,month,year);
}
}
6.自定义泛型
-
基本语法
class 类名<T,R...>{
//...表示可以有多个成员
}
-
注意细节
-
普通成员可以使用泛型(属性或方法)
-
使用泛型的数组不能初始化
-
静态方法中不能使用类的泛型
-
泛型类的类型,是在创建对象时确定的(因为创建对象时,需要指定确定泛型)
-
如果在创建对象时,没有指定泛型,默认为Object
-
-
package com.mdklea.customgeneric; public class CustomGeneric_ { public static void main(String[] args) { } } //解读 //1. Tiger后面有泛型,所以我们把 Tiger称为自定义泛型类 //2.T R M 是泛型的标识符,一般是单个的大写字母 //3.泛型的标识符可以有多个 //4.普通成员可以使用泛型(属性,方法) //5.使用泛型的数组,不能初始化 //6.静态方法不能使用类的泛型 class Tiger<T,R,M> { String name; R r;//属性使用泛型 M m; T t; //因为数组在创建时不能确定T的类型,就无法在内存开空间 //T[] ts = new T[4]; T[] ts; //因为静态是和类相关的,在类加载时,对象还没有创建 //所以如果静态方法和静态属性使用了泛型,JVM就无法初始化 //public static void m1(M m){ //} public Tiger(String name, R r, M m, T t) {//构造器使用泛型 this.name = name; this.r = r; this.m = m; this.t = t; } public String getName() { return name; } public void setName(String name) { this.name = name; } 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; } public T getT() { return t; } public void setT(T t) { this.t = t; } }
-
自定义泛型接口
-
基本语法
interface 接口名<T,R,...>{ }
-
注意细节
-
接口中,静态成员也不能使用泛型(这个和泛型类的规定一样)
-
泛型接口的类型,在继承接口或者实现接口时确定
-
没有指定类型,默认为Object
-
package com.mdklea.customgeneric; public class CustomInterfaceGeneric { public static void main(String[] args) { } } /* 泛型接口使用的说明 1. 接口中,静态成员不能使用泛型 2. 泛型接口的类型,是在继承接口或者实现接口时确定的 3. 没有指定类型,默认为Object */ interface IUsb<U,R> { //接口中的成员都是静态性质的 int i = 10; //U name; 不能这样使用 //普通方法中,可以使用接口泛型 R get(U u); void hi(R r); void run(R r1,R r2,U u1,U u2); //JDK8中,可以在接口中,使用默认方法,也是可以使用泛型的 default R methdod(U u) { return null; } } //在继承接口时,确定泛型接口类型 interface IA extends IUsb<String ,Double> {} //当我们实现IA接口时,因为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) { } } //没有指定类型,默认为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) { } }
-
-
自定义泛型方法
-
基本语法
修饰符 <T,R...> 返回类型 方法名(参数列表) {}
-
注意细节
-
泛型方法可以定义在普通类中,也可以定义在泛型类中
-
当泛型方法被调用时,类型会确定
-
public void eat(E e){},修饰符后没有<T,R...> eat方法不是泛型方法,而是使用了泛型。
-
-
package com.mdklea.customgenericmethod; import java.util.ArrayList; @SuppressWarnings({"all"}) public class CustomGenericMethod { public static void main(String[] args) { Car car = new Car(); //当泛型方法被调用时,类型会确定 car.fly("宝马",100); Fish<String, ArrayList> fish = new Fish<>(); fish.hello(new ArrayList(),12.4f); } } /* 泛型方法的使用: 1. 泛型方法可以定义在普通类中,也可以定义在泛型类中 2. 当泛型方法被调用时,类型会确定 3. public void eat(E e) {} 修饰符后没有<T,R>eat方法不是泛型方法, 而是使用了泛型。 */ class Car { public void run() {//普通方法 } //泛型方法可以定义在普通类中,也可以定义在泛型类中 public<T,R> void fly(T t,R r) {//泛型方法 System.out.println(t.getClass()); System.out.println(r.getClass()); } } class Fish<T,R> { //泛型类 public void run() { } public<U,M> void eat(U u, M m) { //泛型方法 } //说明 //1. 下面这个方法不是泛型方法 //2. 是hi方法使用了类声明的 泛型 public void hi(T t) { } //泛型方法可以使用类声明的泛型,也可以使用自己声明的泛型 public<K> void hello(R r,K k) { System.out.println(r.getClass()); System.out.println(k.getClass()); } }
-
7.泛型的继承和通配符
-
泛型的继承和通配符说明:
-
泛型不具备继承性
List<Object> list = new ArrayList<String> (); //对吗?
-
<?>:支持任意泛型类型
-
<? extends A>:支持A类以及A类的子类,规定了泛型的上限
-
<? super A>:支持A类以及A类的父类,不限于直接父类,规定了泛型的下限
-
8.JUnit单元测试类
-
为什么需要JUnit
-
一个类有很多功能代码需要测试,为了测试,就需要写到main方法中
-
如果有多个功能代码测试,就需要来回注销,切换很麻烦
-
如果可以直接运行一个方法,就方便很多,并且可以给出相关信息就好了 -> Junit
-
-
基本介绍
-
JUnit是一个Java语言的单元测试框架
-
多少java的开发环境都已经集成了JUnit作为单元测试的工具
-
8.章节练习
package com.mdklea.homework;
import org.junit.jupiter.api.Test;
import java.util.*;
@SuppressWarnings({"all"})
public class HomeWork01 {
public static void main(String[] args) {
}
@Test
public void testList() {
DAO<User> dao = new DAO<>();
dao.save("001",new User(1,10,"jack"));
dao.save("002",new User(2,20,"mary"));
dao.save("003",new User(3,30,"smith"));
System.out.println(dao.list());
dao.update("003",new User(3,20,"mdk"));
System.out.println(dao.list());
dao.delete("001");
System.out.println(dao.list());
}
}
@SuppressWarnings({"all"})
class DAO<T> {
private Map<String, T> map = new HashMap<>();
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) {
if (map.containsKey(id)){
map.put(id,entity);
}else {
System.out.println("map中无此对象!");
}
}
public List<T> list() {
Collection<T> values = map.values();
return new ArrayList<>(values);
}
public void delete(String id) {
map.remove(id);
}
}
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 + '\'' +
'}';
}
}