目录
一.泛型的语法
1.传统的方法遍历集合存在的问题
需求
传统代码实现需求
package com.Generics;
import java.util.ArrayList;
public class Generics01 {
public static void main(String[] args) {
// 使用传统的方法来解决
ArrayList list = new ArrayList();
list.add(new Dog("旺财",10));
list.add(new Dog("发财",1));
list.add(new Dog("小黄",5));
for (Object o : list) {
// 进行类型的强转
Dog dog = (Dog)o;
// 输出
System.out.println("年龄:"+dog.getAge()+" 名字:"+dog.getName());
}
}
}
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;
}
}
运行结果
年龄:10名字:旺财
年龄:1名字:发财
年龄:5名字:小黄
问题:如果程序员不小心加入了不同类型的数据,会发生类型转换异常ClassCastException,存在一定的隐患,但是编译器不会识别出来,不能对集合当中的数据进行约束。
效率:向下转型的效率也很低
可能出现的问题代码
package com.Generics;
import java.util.ArrayList;
public class Generics01 {
public static void main(String[] args) {
// 使用传统的方法来解决
ArrayList list = new ArrayList();
list.add(new Dog("旺财",10));
list.add(new Dog("发财",1));
list.add(new Dog("小黄",5));
// 程序员不小心加入了一只猫
list.add(new Cat("小白",2));
for (Object o : list) {
// 这里的强转会出现问题 数据量太大的话,转型会消耗大量的内存 效率很低
Dog dog = (Dog)o;
System.out.println("年龄:"+dog.getAge()+" 名字:"+dog.getName());
}
//
}
}
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;
}
}
class Cat{
private String name;
private int age;
public Cat(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;
}
}
运行结果
年龄:10 名字:旺财
年龄:1 名字:发财
年龄:5 名字:小黄
Exception in thread "main" java.lang.ClassCastException: com.Generics.Cat cannot be cast to com.Generics.Dog
at com.Generics.Generics01.main(Generics01.java:17)
总结
2.使用泛型来解决传统集合添加元素带来的问题
泛型优化之后的代码
package com.Generics;
import java.util.ArrayList;
public class Generics02 {
public static void main(String[] args) {
ArrayList<Dog> dogs = new ArrayList<>();
// 使用泛型来解决问题
dogs.add(new Dog("旺财",10));
dogs.add(new Dog("发财",1));
dogs.add(new Dog("小黄",5));
// 加入其他类型的数据会有编译错误提示 也就是说 我们这个集合当中只能存放我们指定的对象
// dogs.add(new Cat("小白",2));
for (Dog dog : dogs) {
// 访问集合当中的数据也不用进行类型的转换效率提高
System.out.println("年龄 :"+dog.getAge()+" 名字:"+dog.getName());
}
}
}
运行结果
年龄 :10 名字:旺财
年龄 :1 名字:发财
年龄 :5 名字:小黄
3.泛型使用介绍
3.1基本使用
解释泛型的代码
package com.Generics;
public class Generics03 {
public static void main(String[] args) {
// 如何理解 明明没有构造器 E可以看成是一个变量 一个关于类的变量 和int与整数一样
Person<String> p1 = new Person<>("韩顺平教育");
System.out.println(p1.f());
// 等效于 在编译期间确定E的类型
// class Person{
// String e;
//
// public Person(String e) {
// this.e = e;
// }
//
// public String f(){
// return e;
// }
// }
// 像是一个万能的关于类类型的变量
Person<Integer> integerPerson = new Person<>(100);
System.out.println(integerPerson.f());
}
}
//泛型:在类声明是通过某一个标识,来标识类当中某一个属性的值。
// 或者是某个方法的返回值类型,或者是参数类型
class Person<E>{
// E表示e的数据类型,该数据类型在定义person对象的时候指定,即在编译期间就知道e是什么类型了
// 感觉像是类当中一个很奇怪的属性
E e;
public Person(E e) { //E可以是参数类型
this.e = e;
}
public E f(){ //E也可以是返回值类型
return e;
}
}
运行的结果
韩顺平教育
100
3.2泛型的语法应用
练习
代码
package com.Generics;
import java.util.*;
public class GenericsExercise {
public static void main(String[] args) {
HashSet<Student> students = new HashSet<>();
students.add(new Student("小王",10));
students.add(new Student("小明",15));
students.add(new Student("小张",12));
//iter是最好用的快捷键
for (Student student : students) {
System.out.println(student);
}
HashMap<String, Student> hashMap = new HashMap<>();
hashMap.put("小王",new Student("小王",10));
hashMap.put("小明",new Student("小明",15));
hashMap.put("小张",new Student("小张",12));
/*这里的k和V就是String和Student 在编译的时候就已经指定了
* 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();
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());
}
// 感觉迭代器没有强化for循环好使
for (Map.Entry<String, Student> entry : entries) {
System.out.println(entry.getKey()+" "+entry.getValue());
}
}
}
class Student{
private String name;
private int age;
public Student(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 "Student{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
运行结果
Student{name='小张', age=12}
Student{name='小明', age=15}
Student{name='小王', age=10}
小明 Student{name='小明', age=15}
小王 Student{name='小王', age=10}
小张 Student{name='小张', age=12}
小明 Student{name='小明', age=15}
小王 Student{name='小王', age=10}
小张 Student{name='小张', age=12}
4.泛型的使用细节
代码
package com.Generics;
import java.util.ArrayList;
import java.util.List;
public class GenericsDetail {
public static void main(String[] args) {
List<Integer> list = new ArrayList<Integer>();
// List<int> list = new ArrayList<int>(); 错误的,只能使用引用数据类型,不能使用基本数据类型
Pig<A> aPig = new Pig<>(new A()); //可以是指定的类型A
Pig<A> aPig1 = new Pig<>(new B()); //也可以是指定类型的子类
// 运行类型
aPig.f();
aPig1.f();
// 泛型的使用形式
ArrayList<Integer> integers = new ArrayList<Integer>();
List<Integer> list1 = new ArrayList<Integer>();
// 在实际开发当中我们往往进行简写 编译器会自行进行推断
ArrayList<Integer> integers1 = new ArrayList<>();
List<Integer> list2 = new ArrayList<>();
// 这样写泛型默认的类型是Object类型
ArrayList integers2 = new ArrayList();
Tiger tiger = new Tiger();
/*等价于
* class Tiger{
Object e;
public Tiger() {
}
public Tiger( Object e) {
this.e = e;
}
}*/
}
}
class Tiger<E>{
E e;
public Tiger() {
}
public Tiger(E e) {
this.e = e;
}
}
class A{
}
class B extends A{}
class Pig<E>{
E e;
public Pig(E e) {
this.e = e;
}
public void f(){
System.out.println(e.getClass());
}
}
运行结果
class com.Generics.A
class com.Generics.B
5.泛型课堂练习
代码
MyDate类(进行了优化,实现了compareTo接口)
package com.Generics;
public class MyDate implements Comparable<MyDate>{
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;
}
// 方便比较
@Override
public int compareTo(MyDate myDate) {
int y = year-myDate.getYear();
if (y!=0){
return y;
}
int m = month-myDate.getMonth();
if (m!=0){
return m;
}
return day-myDate.getDay();
}
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 +
'}';
}
}
Employee类
package com.Generics;
public class Employee {
private String name;
private double sal;
private MyDate myDate;
public Employee(String name, double sal, MyDate myDate) {
this.name = name;
this.sal = sal;
this.myDate = myDate;
}
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 getMyDate() {
return myDate;
}
public void setMyDate(MyDate myDate) {
this.myDate = myDate;
}
@Override
public String toString() {
return "Employee{" +
"name='" + name + '\'' +
", sal=" + sal +
", myDate=" + myDate +
'}';
}
}
GenericsExercise02类
package com.Generics;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.Date;
public class GenericsExercise02 {
public static void main(String[] args) {
ArrayList<Employee> employees = new ArrayList<>();
employees.add(new Employee("tom",20000,new MyDate(2000,11,11)));
employees.add(new Employee("jack",12000,new MyDate(2001,12,12)));
employees.add(new Employee("hsp",18000,new MyDate(1980,10,10)));
for (Employee employee : employees) {
System.out.println(employee);
}
System.out.println("======对与员工进行排序======");
employees.sort(new Comparator<Employee>() {
@Override
public int compare(Employee o1, Employee o2) {
if (!(o1 instanceof Employee) && (o2 instanceof Employee)){
System.out.println("类型不正确。。");
return 0;
}
// 比较name
int i = o1.getName().compareTo(o2.getName());
if (i!=0){
return i;
}
return o1.getMyDate().compareTo(o2.getMyDate());
}
});
System.out.println("===排序后的结果=====");
System.out.println(employees);
}
}
运行结果
Employee{name='tom', sal=20000.0, myDate=MyDate{year=2000, month=11, day=11}}
Employee{name='jack', sal=12000.0, myDate=MyDate{year=2001, month=12, day=12}}
Employee{name='hsp', sal=18000.0, myDate=MyDate{year=1980, month=10, day=10}}
======对与员工进行排序======
===排序后的结果=====
[Employee{name='hsp', sal=18000.0, myDate=MyDate{year=1980, month=10, day=10}}, Employee{name='jack', sal=12000.0, myDate=MyDate{year=2001, month=12, day=12}}, Employee{name='tom', sal=20000.0, myDate=MyDate{year=2000, month=11, day=11}}]
二.自定义泛型
1.自定义泛型类的规范
代码解析
package com.customgenerics;
public class CustomGenerics {
public static void main(String[] args) {
}
}
//类名后面有泛型,一般称之为自定义泛型类
//泛型一般是单个大写字母,可以指定更多个泛型
class Tiger<T,R,M>{
String name;
// 属性使用泛型
T t;
R r;
M m;
// T[] n = new T[8]; 不能直接实例化,T的类型无法确定,没有办法在内存当中开辟空间
// 静态方法和静态属性是和类相关的,在类加载是初始化,而泛型初始化是在创建实例时完成的。JVM无法完成类的初始化
// static R r1;
// public static void m1(M m){
//
// }
// 构造器使用泛型
public Tiger(String name, T t, R r, M m) {
this.name = name;
this.t = t;
this.r = r;
this.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;
}
}
练习题
代码
package com.customgenerics;
public class CustomGenericsExercise {
public static void main(String[] args) {
// 后面()当中调用的是构造器
// 对应的类型T= double R=string M=integer
Tiger01<Double,String,Integer> john = new Tiger01<>("john");
john.setT(10.9);
// john.setT("nihao"); 类型不匹配
System.out.println(john);
Tiger01 tiger01 = new Tiger01("join---");
// T=Object R=Object M=Object 可以传入任意的类型
tiger01.setT("yy");
tiger01.setT(10.1);
System.out.println(tiger01);
}
}
class Tiger01<T,R,M>{
String name;
// 属性使用泛型
T t;
R r;
M m;
// 构造器使用泛型
public Tiger01(String name) {
this.name = name;
}
public Tiger01( T t, R r, M m) {
this.t = t;
this.r = r;
this.m = m;
}
public Tiger01(String name, T t, R r, M m) {
this.name = name;
this.t = t;
this.r = r;
this.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;
}
@Override
public String toString() {
return "Tiger01{" +
"name='" + name + '\'' +
", t=" + t +
", r=" + r +
", m=" + m +
'}';
}
}
运行结果
Tiger01{name='john', t=10.9, r=null, m=null}
Tiger01{name='join---', t=10.1, r=null, m=null}
2.自定义泛型接口的规范
基本语法
代码
package com.customgenerics;
public class CustomInterfaceGenerics {
public static void main(String[] args) {
}
}
interface IUsb<U,R>{
int n =10;
// U u ="name"; 不可以这样写 接口当中的成员是静态的
// 普通方法可以直接使用接口泛型 当做返回值类型
U get();
void hi(R r);
void run(R r,R r1,U u,U u1);
// jdk8 可以使用Default来定义方法体
default R method(U u){
return null;
}
}
//在继承的时候实现泛型
interface IA extends IUsb<String ,Double>{
}
class A implements IA{
// U,R在实现类当中直接被替换为了具体的类
@Override
public String get() {
return null;
}
@Override
public void hi(Double aDouble) {
}
@Override
public void run(Double aDouble, Double r1, String s, String u1) {
}
}
//默认的类型是Object
class BB implements IUsb{
@Override
public Object get() {
return null;
}
@Override
public void hi(Object object) {
}
@Override
public void run(Object object, Object r1, Object object2, Object u1) {
}
}
//在实现的时候指定接口的泛型类型
class CC implements IUsb<String ,Integer>{
@Override
public String get() {
return null;
}
@Override
public void hi(Integer integer) {
}
@Override
public void run(Integer integer, Integer r1, String s, String u1) {
}
}
3.自定义泛型方法
应用代码
package com.customgenerics;
public class CustomMethodGenerics {
public static void main(String[] args) {
Car car = new Car();
car.fly("宝马",100); //自动装箱,编译器自动的确定类型
car.fly(100.1,100);
Fish<String, Integer> fish = new Fish<>();
fish.cry("小鸟",10);
}
}
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){
}
// 使用了类声明的泛型,并不是泛型方法
public void hi(T t){
}
// 泛型方法可以使用类的泛型,也可以使用自己方法的泛型 泛型是一种可以接收数据类型的数据类型
public <A > void cry(A a,R r){
System.out.println(a.getClass());//String
System.out.println(r.getClass());//Integer
}
}
运行结果
class java.lang.String
class java.lang.Integer
class java.lang.Double
class java.lang.Integer
class java.lang.String
class java.lang.Integer
4.泛型方法练习
代码
package com.customgenerics;
public class CustomMethodGenericsExercise {
public static void main(String[] args) {
Apple<String, Integer, Double> apple = new Apple<>();
apple.fly(10);
apple.fly(new Dog());
}
}
class Apple<T,R,M>{
public <E> void fly(E e){
System.out.println(e.getClass().getSimpleName());
}
// public void hi(H h){} 没有定义泛型U
public void hi(T t){}
}
class Dog{}
结果
Integer
Dog
三.泛型的继承与通配符
泛型不具备继承性
代码
package com.customgenerics;
import java.util.ArrayList;
import java.util.List;
public class GenericsExTends {
public static void main(String[] args) {
// 类的继承关系
Object o =new String("小王");
// 泛型是没有继承关系的
// List<Object> list = new ArrayList<String>(); 错误的写法
// 下面三个方法的使用
ArrayList<Object> objects = new ArrayList<>();
ArrayList<String> strings = new ArrayList<>();
ArrayList<AA> aas = new ArrayList<>();
ArrayList<EE> ees = new ArrayList<>();
ArrayList<FF> ffs = new ArrayList<>();
// List<?> list 可以接受任意的泛型类型
printCollection1(objects);
printCollection1(strings);
printCollection1(aas);
printCollection1(ees);
printCollection1(ffs);
// List<? extends AA> list 标识只能接受AA或者AA的子类
// printCollection2(objects); 编译错误
// printCollection2(strings); 编译报错
printCollection2(aas);
printCollection2(ees);
printCollection2(ffs);
// List<? super AA> list 只能接受AA的父类
printCollection3(objects);
// printCollection3(strings); 编译错误
printCollection3(aas);
// printCollection3(ees); 错误
// printCollection3(ffs); 错误
}
// 编写几个方法
// 任意泛型的类型都可以填进去
public static void printCollection1(List<?> list){
for (Object o : list) {
System.out.println(o);
}
}
// 标识只能接受AA或者AA的子类
public static void printCollection2(List<? extends AA> list){
for (Object o : list) {
System.out.println(o);
}
}
// 只能接受AA的父类
public static void printCollection3(List<? super AA> list){
for (Object o : list) {
System.out.println(o);
}
}
}
class AA{}
class EE extends AA{}
class FF extends EE{}
四.课后作业
1.课后作业
代码
HomeWork类
package com.homework;
import org.junit.jupiter.api.Test;
import java.util.List;
public class HomeWork {
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,18,"tom"));
userDAO.save("003",new User(3,15,"smith"));
List<User> list = userDAO.list();
System.out.println(list);
userDAO.update("003",new User(3,58,"milan"));
userDAO.delete("001");
System.out.println("=======修改后======");
list = userDAO.list();
System.out.println(list);
System.out.println(userDAO.get("003"));
}
}
DAO类
package com.homework;
import java.util.*;
public class DAO<T> {
private Map<String ,T> map = new HashMap<>(); //没有private会报错
public T get(String id) {
return map.get(id);
}
public void update(String id,T entity){
map.put(id,entity);
}
// 返回map当中存放所有的T对象 遍历map将map当中所有的v封装到ArrayList当中
public List<T> list(){
// 创建List
List<T> list = new ArrayList<>();
// 遍历map
Set<String> keySet = map.keySet();
for (String s : keySet) {
list.add(map.get(s));
}
return list;
}
public void delete(String id){
map.remove(id);
}
public void save(String id,T entity){ //把entity存放到map当中
map.put(id, entity);
}
}
User类
package com.homework;
public 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 + '\'' +
'}';
}
}
运行结果
[User{id=1, age=10, name='jack'}, User{id=2, age=18, name='tom'}, User{id=3, age=15, name='smith'}]
=======修改后======
[User{id=2, age=18, name='tom'}, User{id=3, age=58, name='milan'}]
User{id=3, age=58, name='milan'}
2.内容梳理
泛型的作用:解决遍历集合时候出现的类型转换效率,规范集合当中存放的数据类型,提高读取集合元素的效率。
泛型的理解:可以转化为任何类的一个形参,关于类的类型的形参
泛型的使用时机:在对象创建,接口实现的时候初始化,因此不能和static进行混用。
泛型的局限:泛型适用于继承机制,因此,泛型引入和通配符机制,来进行完善。
泛型的使用规范:父类泛型确定的情况下,子类会继承父类的泛型类型,接口也一样;如果没有指定泛型的类型,那么泛型为默认类型Object类;方法当中的参数可以采用方法自己定义的泛型类型也可以采用本类定义的泛型类型,没有定义的泛型类型是不能使用的;类的泛型在创建对象时确定,方法的泛型在创建方法是确定。
泛型的书写规范:一般是一个大写的字母
五.新使用的一个jr包JUnit
1.JUnit的作用
2.如何使用JUnit
根据提示选择点击JUnit5.4,再点击Ok就可以了
引入成功,警告小时
点击小箭头
直接运行m1方法,不用main方法调用