JAVA 泛型

泛型

一、泛型引入

package com.hspedu.generic;

import java.util.ArrayList;

/**
 * @author DL5O
 * @version 1.0
 */
@SuppressWarnings("all")
public class Generic01 {
    public static void main(String[] args) {
        ArrayList list = new ArrayList();
        list.add(new Dog("旺财",5));
        list.add(new Dog("发财",1));
        list.add(new Dog("小黄",6));
        list.add(new Cat("招财猫",8));//加入一只猫
        //遍历
        for (Object o :list) {
            Dog dog = (Dog)o;//向下转型
            System.out.println("name=" + dog.getName() + ",age=" + dog.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 int getAge() {
        return 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 int getAge() {
        return age;
    }
}

使用传统方法的问题分析

  1. 不能对加入到集合ArrayList中的数据类型进行约束(不安全
  2. 遍历的时候,需要进行类型转换,如果集合中的数据量较大,对效率有影响

二、泛型入门

package com.hspedu.generic.improve;

import java.util.ArrayList;

/**
 * @author DL5O
 * @version 1.0
 */
@SuppressWarnings("all")
public class Generic02 {
    public static void main(String[] args) {
        //引入泛型
        //传统方法解决 ===> 使用泛型
        //1.当我们 ArrayList<Dog> 表示存放到ArrayList 集合中的元素是Dog类型
        //2.如果编译器发现添加的类型,不满足要求,就会报错
        //3.在遍历的时候,可以直接取出 Dog 类型,而不是Object类型

        ArrayList<Dog> list = new ArrayList<Dog>();
        list.add(new Dog("旺财",5));
        list.add(new Dog("发财",1));
        list.add(new Dog("小黄",6));
//        list.add(new Cat("招财猫",8));//加入一只猫,使用泛型后,就加入不了了

        System.out.println("===使用泛型===");
        for (Dog dog :list) {
            System.out.println("name=" + dog.getName() + ",age=" + dog.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 int getAge() {
        return 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 int getAge() {
        return age;
    }
}
  1. 当我们 ArrayList 表示存放到ArrayList 集合中的元素是Dog类型
  2. 如果编译器发现添加的类型,不满足要求,就会报错
  3. 在遍历的时候,可以直接取出 Dog 类型,而不是Object类型

小结:

  • 编译时,检查添加元素的类型,提高了安全性
  • 减少了类型转换的次数,提高效率
  • 不再提示编译警告

不使用泛型

Dog -加入-> Object -取出 -> Dog//放到ArrayList中会先转成Object,在取出时,还需要转成Dog

使用泛型

Dog -> Dog -> Dog//放入和取出时,不需要进行类型的转换,提高效率


三、泛型介绍

老韩理解:泛(广泛)型(类型)=> Integer, String,Dog

  • 即可以表示其他数据的类型的数据类型
  1. 泛型又称参数化类型,是Jdk5.0出现的新特性,解决数据类型的安全性问题

  2. 在类声明或实例化时只要指定好需要的具体的类型即可。

  3. Java泛型可以保证如果程序在编译时没有发出警告,运行时就不会产生,ClassCastException异常。同时,代码更加简洁、健壮

  4. 泛型的作用是:可以在类声明时通过一个标识表示类中某个属性的类型,或者是某个方法的返回值的类型,或者是参数类型。[有点难,举例Generic03.java]

注意:(特别强调!!!)

  • E的数据类型在定义的Person的时候指定,即在编译期间,就确定E是什么类型
package com.hspedu.generic;

/**
 * @author DL5O
 * @version 1.0
 */
public class Generic03 {
    public static void main(String[] args) {
        Person<String> ywl = new Person<>("ywl");

        ywl.show();//String

        /*
            可以这样理解,上面的Person类
            把E 替换成了String,可以这样理解,但这样不太准确
         */

        Person<Integer> integerPerson = new Person<>(100);
        //E 这个泛型在定义的时候就知道了
        integerPerson.show();//Interger
    }
}

//泛型的作用:可以在类声明时通过一个标识表示类中某个属性的类型,
// 或者是某个方法的返回值的类型,或者是参数类型。
class Person<E>{
    E s;
    //E表示 s的数据类型,该数据类型是在定义Person对象的时候指定的,
    // 即在编译期间就确定了E是什么类型了

    public Person(E s) {//E也可以是参数类型中体现
        this.s = s;
    }

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

    public void show(){
        System.out.println(s.getClass());//输出显示S的运行类型
    }
}

四、泛型的语法

interface 接口{} 和 class <K,V>{}

比如:List,ArrayList

说明:

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

泛型的实例化

  1. List strList = new ArrayList();
  2. Iterator iterator = customers.iterator();
package com.hspedu.generic;

import java.util.*;

/**
 * @author DL5O
 * @version 1.0
 */
public class GenericExercise {
    public static void main(String[] args) {
        Student ywl = new Student("ywl", 21);
        Student zsj = new Student("zsj", 20);
        Student sky = new Student("sky", 19);

        //HashMap 遍历方式
//        System.out.println("====HashMap遍历方式====");
        HashMap<String, Student> map = new HashMap<String, Student>();
        /*
            public class HashMap<K,V>
         */
        map.put("ywl", ywl);
        map.put("zsj", zsj);
        map.put("sky", sky);
        System.out.println("====keySet====");


        //迭代器 EntrySet
        Set<Map.Entry<String, Student>> entries = map.entrySet();
        /*
             public Set<Map.Entry<K,V>> entrySet() {
                Set<Map.Entry<K,V>> es;
                return (es = entrySet) == null ? (entrySet = new EntrySet()) : es;
            }
         */
        Iterator<Map.Entry<String, Student>> iterator1 = entries.iterator();
        while (iterator1.hasNext()) {
            Map.Entry<String, Student> student = iterator1.next();
            System.out.println(student.getKey() + "-" + student.getValue());
        }

        //HashSet
        System.out.println("====HashSet====");
        HashSet<Student> students = new HashSet<>();
        students.add(ywl);
        students.add(zsj);
        students.add(sky);

        //迭代器遍历
        Iterator<Student> iterator = students.iterator();
        while (iterator.hasNext()) {
            Student student = iterator.next();
            System.out.println(student);
        }

        System.out.println("====增强for循环====");
        //for循环遍历
        for (Student student : students) {
            System.out.println(student);
        }

    }
}

class Student {
    public String name;
    public int age;

    public Student(String name, int age) {
        super();
        this.name = name;
        this.age = age;
    }

    @Override
    public String toString() {
        return "姓名:" + name + ",年龄:" + age;
    }
}

注意:

  • 定义HashMap的时候,已经把key和value指定了,而迭代器中的k,v也能跟着确定下来,故存进去的都是<String,Student> 的键值对,取出来的也是这样的一对键值对
    • 然后使用getValue方法可以获得Student对象,getKey可以获得键值
  • K-> String,Value-> Student;

五、泛型语法和使用

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

    • 这里的E,T...等等只能是引用类型
    • 即给泛型指定类型的时候,只能是引用类型,不能是基本数据类型
    List<Integer> list = new ArrayList<Integer>();//OK
    List<int> list = new ArrayList<int>();//只能是引用类型
    
  2. 在给泛型指定了的具体类型后,可以传入该类型的子类型或者改类型

  3. 泛型使用形式

    List<Interger> list = new ArrayList<>();//推荐写法
    List<Interger> list = new ArrayList<Interger>();
    
    • 如果这样写List list = new ArrayList();他的默认泛型是Object;
      • 它等价于:List<Object> list = new ArrayList<>();

课堂练习题

package com.hspedu.generic.exercise;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.Iterator;

/**
 * @author DL5O
 * @version 1.0
 */
public class GenericExercise01 {
    public static void main(String[] args) {
        ArrayList<Employee> employees = new ArrayList<>();
        employees.add(new Employee("小龙23", 5000, new MyDate(2000, 9, 9)));
        employees.add(new Employee("大龙23", 6000, new MyDate(2001, 10, 9)));
        employees.add(new Employee("小张", 7500, new MyDate(2002, 11, 9)));
        employees.add(new Employee("小宋", 6500, new MyDate(2001, 5, 9)));

        System.out.println("====排序前====");
        for (Employee e : employees) {
            System.out.println(e);
            System.out.println();
        }

        System.out.println("====进行排序====");
        //进行排序操作
        //按照name进行排序,如果name相同,就按照生日的先后顺序进行排序
        Collections.sort(employees, new Comparator<Employee>() {
            @Override
            public int compare(Employee e1, Employee e2) {
//                int ret = e1.getName().compareTo(e2.getName());
                int ret = e1.getName().length() - e2.getName().length();
                //长度也按从小到大来比较
                if (ret != 0) {
                    return ret;
                }

                //如果名字/名字长度相同
                //出生年月按从大到下进行比较
                //下面是对birthday的比较,因此,我们最好把这个比较,放在MyDate类完成
                //封装后,将来可维护性和复用性,就大大增强
                MyDate e1Birthday = e1.getBirthday();
                MyDate e2Birthday = e2.getBirthday();
                return e1Birthday.compareTo(e2Birthday);
            }
        });

        Iterator<Employee> iterator = employees.iterator();
        while (iterator.hasNext()) {
            Employee e = iterator.next();
            System.out.println(e);
            System.out.println();
        }
    }

    /*//对ArrayList 集合中的元素进行排序
    public static void sort(ArrayList<Employee> employees){

    }*/
}

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 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 "姓名:" + name + "\n工资:" + sal + "\n出生日期:" + birthday;
    }
}

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;
    }

    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 year + "." + month + "." + day;
    }

    @Override
    public int compareTo(MyDate o) {//把对年月日year-month-day的比较放在这里
        /*if (year > o.getYear()) {
            return 1;
        } else if (month > o.getMonth()) {
            return 1;
        } else if (day > o.getDay()) {
            return 1;
        } else if (year < o.getYear()
                || month < o.getMonth() ||
                day < o.getDay()) {
            return -1;
        } else {
            return 0;
        }*/

        //老师的思路
        //如果出生年份不一样
        int yearMinus = year - o.getYear();
        if (yearMinus != 0) {
            return yearMinus;
        }

        //如果出生年份一样,但月份不一样
        int monthMinus = month - o.getMonth();
        if (monthMinus != 0) {
            return monthMinus;
        }

        //如果出生年份月份一样,就比较日期
        int dayMinus = day - o.getDay();
        return dayMinus;
    }
}

六、自定义泛型

自定义泛型类

基本语法:

class 类名<T,R...>{//可以有多个
    成员
}

注意细节:

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

应用案例:

package com.hspedu.generic.cusotmgeneric;

/**
 * @author DL5O
 * @version 1.0
 */
public class CustomGeneric {
    public static void main(String[] args) {
        
    }
}

//解读
//1.Tiger后面有泛型,所有我们把Tiger就称为 自定义泛型类
//2.T,R,M泛型标识符,一般那都是字母大写
//3.泛型可以有多个
//4.普通成员,属性,方法都可以使用泛型
class Tiger<T,R,M>{
    String name;
    R r; //属性
    M m;
    T t;

    //因为数组在new 的时候,不能确定T的类型,就无法在内存中开空间
    T [] ts;//使用泛型的不允许直接初始化

    public Tiger(String name){
        this.name = name;
    }

    public Tiger(String name, R r, M m, T t) {
        this.name = name;
        this.r = r;
        this.m = m;
        this.t = t;
    }


    //在加载tiger类的时候,M泛型仍然不确定是怎么样的类型
    //因为是静态的和类相关的,在类加载时,对象可能还没有创建
    //所有静态方法和静态属性使用了泛型,JVM 就无法完成初始化
  /*  static R r2;
    public static void m1(M m){

    }*/

    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;
    }
}
  1. Tiger后面有泛型,所有我们把Tiger就称为 自定义泛型类
  2. T,R,M为泛型的标识符,一般是单个大写字母
  3. 泛型标识符可以有多个

自定义泛型接口

基本语法

interface 接口名<T,R...>{
    
}
package com.hspedu.generic.cusotmgeneric;

/**
 * @author DL5O
 * @version 1.0
 */
public class CustomInterfaceGeneric {
    public static void main(String[] args) {

    }
}

/**
 *  泛型接口的注意事项和说明
 *  1.接口中,静态成员也不能使用泛型(这个和泛型类规定一样)
 */

//在继承接口 指定泛型接口的类型
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
//Object 会替换 U,R
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) {

    }
}


interface IUsb<U,R>{
    int n = 10;
    //U name =""; 不能使用
    //在普通方法中,可以使用泛型接口
    R get(U u);

    void hi(R r);

    void run(R r1, R r2 , U u1, U u2);

    //在jdk8.0中,可以在接口中,使用默认方法,也是可以使用泛型的
    default R method(U u){
        return null;
    }

}

注意细节:

  1. 接口中,静态成员也不能使用泛型(这个和泛型类规定一样)
    • 在接口中的属性都是静态的,公开的,finale的
  2. 泛型接口的类型,在继承接口或者实现接口时确定
  3. 没有指定类型,默认为Object

自定义泛型方法

基本语法

修饰符 <T,R..>返回类型 方法名(参数列表){
    
}
package com.hspedu.generic.cusotmgeneric;

import java.util.ArrayList;

/**
 * @author DL5O
 * @version 1.0
 */
public class CustomGenericMethodGeneric {
    public static void main(String[] args) {
        Car car = new Car();
        car.fly("你好", 10);//这里的是10会进行自动装箱
        //当调用方法时,传入参数,编译器,就会确定类型
        System.out.println("=============");
        car.fly(300, 10.3);

        System.out.println("=============");
        //T->String R->ArrayList
        Fish<String, ArrayList> fish = new Fish<>();
        fish.hello(new ArrayList(),11.3f);
    }
}

//泛型方法,可以定义在普通类中,也可以定义在泛型类中
class Car {//非常普通的类

    public void run() {//普通方法

    }

    //解读
    //1.<T,R> 为泛型标识符
    //2.提供给 fly 使用的
    public <T, R> void fly(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) {//泛型方法

    }

    //说明
    //1.下面hi方法不是泛型方法
    //2.是hi方法使用了类声明的泛型 T
    public void hi(T t) {
    }

    //泛型方法,可以使用类声明的泛型,也可以使用自己声明的泛型
    public <K> void hello(R r,K k) {
        System.out.println(r.getClass());//ArrayList
        System.out.println(k.getClass());//
    }

}

注意细节:

  1. 泛型方法,可以定义在普通类中,也可以定义在泛型类中
  2. 当泛型方法被调用时,类型会确定
  3. public void eat(Ee){},修饰符后没有<T,R..>,eat方法不是泛型方法,而是使用了泛型
  4. 泛型方法,可以使用类声明的泛型,也可以使用自己声明的泛型

七、泛型的继承和通配符

  1. 泛型不具备继承性

    List<Object> list = new ArrayList<String>();//不正确
    
  2. :支持任意泛型类型
  3. :**支持A类以及A类的子类,规定了泛型的上限**
  4. :**支持A类以及A类的父类,不限于直接父类,规定了泛型的下限**
package com.hspedu.generic;

import java.util.ArrayList;
import java.util.List;

/**
 * @author DL5O
 * @version 1.0
 * 泛型的继承的通配符
 */
public class GenericExtends {
    public static void main(String[] args) {

        Object xx = new String("xx");
        //泛型没有继承性
//        List<Object> list = new ArrayList<String>();//不正确

        //举例说明下面三个方法的使用
        List<Object> list1 = new ArrayList<>();
        List<String> list2 = new ArrayList<>();
        List<AA> list3 = new ArrayList<>();
        List<BB> list4 = new ArrayList<>();
        List<CC> list5 = new ArrayList<>();

        //如果是List<?> 可以接受任意的泛型类型
        printCollection1(list1);
        printCollection1(list2);
        printCollection1(list3);
        printCollection1(list4);
        printCollection1(list5);

        //如果是printCollection2 List<? extends AA>
//        printCollection2(list1); 语法通不过Object 不是 AA的子类
//        printCollection2(list2); 语法通不过String 不是 AA的子类
        printCollection2(list3);
        printCollection2(list4);
        printCollection2(list5);


        //printCollection3(List<? super AA> c)
        //接受AA 和它的父类,不限于直接父类
        printCollection3(list1);
//        printCollection3(list2); String 和AA 没有任何关系
        printCollection3(list3);
//        printCollection3(list4); BB 为 AA 的子类
//        printCollection3(list5); CC 为 AA 的子类

    }

    //说明: List<?> 表示任意的泛型类型都可以接收
    public static void printCollection1(List<?> c) {
        for (Object object : c) {
            System.out.println(object);
        }
    }

    //List<? extends AA> 表示上限,可以接受AA 或 AA的子类
    public static void printCollection2(List<? extends AA> c) {
        for (Object object : c) {
            System.out.println(object);
        }
    }

    //List<? super AA> 子类名AA : 支持AA类以及AA类的父类,不限于直接父类
    //规定了泛型的下限
    public static void printCollection3(List<? super AA> c) {
        for (Object object : c) {
            System.out.println(object);
        }
    }
}

class AA {

}

class BB extends AA {

}

class CC extends BB {

}

本章作业

image-20220323123206122

Homework01

package com.hspedu.generic.Homewrok;

import java.util.*;

/**
 * @author DL5O
 * @version 1.0
 */
public class Homework01 {
    public static void main(String[] args) {
        User ywl = new User(1, 21, "ywl");
        User sky = new User(2, 20, "sky");
        User zsj = new User(3, 19, "zsj");
        User wjq = new User(4, 18, "wjq");
        User py = new User(4, 20, "py");

        //sava方法的使用
        DAO<User> userDAO = new DAO<>();
        userDAO.save("1", ywl);
        userDAO.save("2", sky);
        userDAO.save("3", zsj);
        userDAO.save("4", wjq);

        System.out.println("====添加之后====");
        //list方法的使用
        List<User> list = userDAO.list();
        DAO.output(list);


        System.out.println("=====修改之后====");
        //update函数的使用
        userDAO.update("4", py);
        DAO.output(list);

        System.out.println("====1号ID用户的信息====");
        System.out.println(userDAO.get("1"));

        System.out.println("====删除之后====");
        userDAO.delete("1");
        list = userDAO.list();
        DAO.output(list);
    }

}

DAO

package com.hspedu.generic.Homewrok;

import java.util.*;

/**
 * @author DL5O
 * @version 1.0
 */
public class DAO<T>{
    private Map<String,T> map = new HashMap<>();

    //保存t对象到Map 成员变量中
    public void save(String id,T entity){
        map.put(id,entity);
    }

    //获取id对应的对象
    public T get(String id){
        return map.get(id);
    }

    //替换map中的key为id的内容,改为entity对象
    public void update(String id,T entity){
        map.replace(id,entity);
//          map.put(id,entity);
    }

    //返回map中存放的所有t对象
    public List<T> list(){
        //创建ArrayList
        List<T> list = new ArrayList<>();

        //第一种
        /*Set<String> keySet = map.keySet();
        for (String key: keySet) {
            list.add(map.get(key));
        }*/

        //第二种
        Collection<T> values = map.values();
        Iterator<T> iterator = values.iterator();
        while (iterator.hasNext()) {
            T t =  iterator.next();
            list.add(t);
        }

        return list;
    }



    /*public Collection<T> list(){
        return map.values();
    }*/

    //删除指定的id对象
    public void delete(String id){
        map.remove(id);
    }



    //输出
    public static void output(List<User> list) {
        Iterator<User> iterator = list.iterator();
        while (iterator.hasNext()) {
            User user =  iterator.next();
            System.out.println(user);
        }
    }
}

User

package com.hspedu.generic.Homewrok;

/**
 * @author DL5O
 * @version 1.0
 */
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 + '\'' +
                '}';
    }
}
posted @ 2022-04-08 14:33  DL50  阅读(27)  评论(0编辑  收藏  举报