【JavaSE】泛型

尚硅谷JavaSE笔记合集

文章名链接
【JavaSE】异常文章地址
【JavaSE】常用类:String、LocalDateTime…文章地址
【JavaSE】枚举文章地址
【JavaSE】注解文章地址
【JavaSE】集合框架文章地址 | HashMap源码解析 | List相关实现类源码解析
【JavaSE】泛型文章地址
【JavaSE】IO流文章地址 | 字符编码详解
【JavaSE】网络编程,BIO需求演进文章地址
【JavaSE】反射文章地址
【JavaSE】jdk8新特性文章地址

一、为什么要有泛型

1.1 使用泛型前

  • 元素存储:集合可以存放进任何元素

    ArrayList list=new ArrayList();
    list.add(123);
    list.add("123");
    list.add("abc");
    
  • 元素获取:集合可以获取到任何类型的元素,赋值时需要进行类型强制转换

    for (Object o : list) {
        int item= (int) o;		//int item=(int)"abc" --> ClassCastException
    }
    

1.2 使用泛型后

  • 元素存储:贴了String标签的集合不能放入其他类型的元素,否则编译不通过

    ArrayList<Integer> list=new ArrayList<>();
    list.add(123);
    //list.add("123");  编译不通过
    
  • 元素获取:从String标签的集合中获取元素不需要再进行类型强制转换

    for (Integer integer : list1) {
        int item=integer;		//元素类型已确定,无需进行类型强制转换
    }
    

1.3 泛型的设计背景

  • 集合容器类在设计阶段/声明阶段不能确定这个容器到底实际存的是什么类型的对象
  • 在JDK1.5之前只能把元素类型设计为Object,JDK1.5之后使用泛型来解决。
    • 因为这个时候除了元素的类型不确定,其他的部分是确定的
      • 例如关于这个元素如何保存,如何管理等是确定的
    • 因此此时把元素的类型设计成一个参数,这个类型参数叫做泛型

1.4 其他说明

  • 所谓泛型,就是允许在定义类、接口时通过一个标识表示类中某个属性的类型或者是某个方法的返回值及参数类型。这个类型参数将在使用时确定(传入实际的类型参数,称为类型实参)。
    • 继承或实现这个接口
    • 用这个类型声明变量、创建对象
  • JDK1.5以后,Java引入了“参数化类型(Parameterizedtype)”的概念,允许我们在创建集合时再指定集合元素的类型
    • 改写了集合框架中的全部接口和类
    • 为这些接口、类增加了泛型支持
    • 从而可以在声明集合变量、创建集合对象时传入类型实参

二、使用泛型

2.1 在声明创建中使用

/**
 * 注意:
 * 	1.泛型实参为基本数据类型时需要使用包装类替换
 * 	2.实例化时没有指明泛型实参,泛型将被擦除。泛型均按照java.lang.Object处理,但不等价于Object
 * 	3.泛型实参不同的引用不能相互赋值
 *      ArrayList<String> list1=null;
 *      ArrayList<Integer> list2=null;
 *      list1=list2;  错误!!!
 * 	4.jdk7新特性:类型推断
 *
 * 建议:
 *	1.泛型要使用就一路都用。要不用就一路都不要用
 *	2.如果定义了泛型形参,在实例化时要指明类的泛型
 */
public class GenericTest {
    //在ArrayList中使用泛型
    @Test
    public void test1(){
        // ArrayList<Integer> list=new ArrayList<Integer>();
        ArrayList<Integer> list=new ArrayList<>();  //jdk7新特性:类型推断
        list.add(1);
        list.add(2);
        // list.add("3");   编译不通过
        Iterator<Integer> iterator = list.iterator();
        while (iterator.hasNext()){
            int item=iterator.next();   //类型确定无需考虑强制类型转换
            System.out.println(item);
        }
    }
    //在HashMap中使用泛型
    @Test
    public void test2(){
        // HashMap<String,Integer> map=new HashMap<String,Integer>();
        HashMap<String,Integer> map=new HashMap<>();    //jdk7新特性:类型推断
        map.put("k1",1);
        map.put("k2",2);
        // map.put("k3","3");    编译不通过
        Set<Map.Entry<String, Integer>> entries = map.entrySet();   //泛型嵌套
        Iterator<Map.Entry<String, Integer>> iterator = entries.iterator();
        while (iterator.hasNext()) {
            Map.Entry<String, Integer> next = iterator.next();
            String key=next.getKey();   //类型确定无需考虑强制类型转换
            int value=next.getValue();  //类型确定无需考虑强制类型转换
            System.out.println(key+"="+value);
        }
    }
}

2.2 在继承中使用

  • 子类不保留父类的泛型:

    • 泛型参数被擦除

      class Father<T1, T2>{}
      // 等价于class Son extends Father<Object,Object>{}
      class Son extends Father{}
      
    • 泛型参数被传入实参

      class Father<T1, T2>{}
      class Son extends Father<Integer, String> {}
      
  • 子类保留父类的泛型:泛型子类

    • 全部保留

      class Father<T1, T2>{}
      class Son<T1, T2> extends Father<T1, T2> {}
      
    • 部分保留

      class Father<T1, T2>{}
      class Son<T1> extends Father<T1, Integer> {}
      

2.3 练习

题目要求

//    Employee类,
//    		private String name
//    		private String age
//			private MyDate birthday
//    		getter,setter,重写toString
//    MyDate类:
//    		private int month;
//    		private int day;
//    		private int year;
//    		getter,setter
  • 创建Employee的5个对象,并把这些对象放入TreeSet集合中(TreeSet需使用泛型来定义),
  • 对集合中的元素进行排序,并遍历输出:
    1. 使Employee继承Comparable接口,并按name排序
    2. 创建TreeSet时传入Comparator对象,按生日日期的先后排序。

不使用泛型

public class Employee implements Comparable{
    @Override
    public int compareTo(Object o) {
        if(o instanceof Employee){
            Employee e=(Employee)o;
            return this.name.compareTo(e.name);
        }
        return 0;
    }
}
public class MyDate implements Comparable{
    @Override
    public int compareTo(Object o) {
        if(o instanceof MyDate){
            MyDate date= (MyDate) o;
            return new Date(year,month,day)
                .compareTo(new Date(date.year,date.month,date.day));
        }
        return 0;
    }
    public static void main(String[] args) {
        //1).使Employee继承Comparable接口,并按name排序
        TreeSet<Employee> set=new TreeSet<>();
        set.add(new Employee("A","1",new MyDate(2022,3,1)));
        set.add(new Employee("B","2",new MyDate(2022,2,1)));
        set.add(new Employee("C","3",new MyDate(2022,1,1)));
        Iterator iterator = set.iterator();
        while (iterator.hasNext()) {
            System.out.println(iterator.next());
        }
        System.out.println("=================================================");
        //2).创建TreeSet时传入Comparator对象,按生日日期的先后排序。
        TreeSet<Employee> set1=new TreeSet<>(new Comparator() {
            @Override
            public int compare(Object o1, Object o2) {
                if (o1 instanceof Employee && o2 instanceof Employee){
                    Employee employee1= (Employee) o1;
                    Employee employee2= (Employee) o2;
                    return employee1.birthday.compareTo(employee2.birthday);
                }
                return 0;
            }
        });
        set1.add(new Employee("A","1",new MyDate(2022,3,1)));
        set1.add(new Employee("B","2",new MyDate(2022,2,1)));
        set1.add(new Employee("C","3",new MyDate(2022,1,1)));
        Iterator iterator1 = set1.iterator();
        while (iterator1.hasNext()) {
            System.out.println(iterator1.next());
        }
    }
}
Employee{name='A', age='1', birthday=MyDate{month='2022', day='3', year='1'}}
Employee{name='B', age='2', birthday=MyDate{month='2022', day='2', year='1'}}
Employee{name='C', age='3', birthday=MyDate{month='2022', day='1', year='1'}}
=================================================
Employee{name='C', age='3', birthday=MyDate{month='2022', day='1', year='1'}}
Employee{name='B', age='2', birthday=MyDate{month='2022', day='2', year='1'}}
Employee{name='A', age='1', birthday=MyDate{month='2022', day='3', year='1'}}

使用泛型

public class Employee implements Comparable{
    @Override
    public int compareTo(Employee o) {
        return this.name.compareTo(o.name);
    }
}
public class MyDate implements Comparable{
    @Override
    public int compareTo(MyDate date) {
        return new Date(year,month,day)
        		.compareTo(new Date(date.year,date.month,date.day));
    }
    public static void main(String[] args) {
        //1).使Employee继承Comparable接口,并按name排序
        TreeSet<Employee> set=new TreeSet<>();
        set.add(new Employee("A","1",new MyDate(2022,3,1)));
        set.add(new Employee("B","2",new MyDate(2022,2,1)));
        set.add(new Employee("C","3",new MyDate(2022,1,1)));
        Iterator iterator = set.iterator();
        while (iterator.hasNext()) {
            System.out.println(iterator.next());
        }
        System.out.println("=================================================");
        //2).创建TreeSet时传入Comparator对象,按生日日期的先后排序。
        TreeSet<Employee> set1=new TreeSet<>(new Comparator<Employee>() {
            @Override
            public int compare(Employee o1, Employee o2) {
                return o1.birthday.compareTo(o2.birthday);
            }
        });
        set1.add(new Employee("A","1",new MyDate(2022,3,1)));
        set1.add(new Employee("B","2",new MyDate(2022,2,1)));
        set1.add(new Employee("C","3",new MyDate(2022,1,1)));
        Iterator iterator1 = set1.iterator();
        while (iterator1.hasNext()) {
            System.out.println(iterator1.next());
        }
    }
}
Employee{name='A', age='1', birthday=MyDate{month='2022', day='3', year='1'}}
Employee{name='B', age='2', birthday=MyDate{month='2022', day='2', year='1'}}
Employee{name='C', age='3', birthday=MyDate{month='2022', day='1', year='1'}}
=================================================
Employee{name='C', age='3', birthday=MyDate{month='2022', day='1', year='1'}}
Employee{name='B', age='2', birthday=MyDate{month='2022', day='2', year='1'}}
Employee{name='A', age='1', birthday=MyDate{month='2022', day='3', year='1'}}

三、自定义泛型结构

建议:泛型要使用就一路都用。要不用就一路都不要用

3.1 直接定义

/**
 * 自定义泛型类
 *
 *	注意:
 *     1.静态方法、静态属性不能使用泛型形参:泛型参数是在类实例化时确定的,静态方法加载时还未确定
 *			比如:static T a;
 *			比如:static T a(T a);
 *     2.不能声明为异常类
 *			比如:class MyException<T> extends Exception{}
 *     3.泛型形参不能作为catch的异常类型
 *			比如:catch(T e)
 *     4.泛型形参不能直接实例化:
 *			比如:T[] arr = new T[10]; 
 *			编译通过:T[] arr = (T[]) new Object[10];
 * @param <T>
 */
public class MyGeneric <T>{
    T genericTest;
    MyGeneric(){}
    MyGeneric(T genericTest){
        this.genericTest=genericTest;
    }

    public T getGenericTest() {
        return this.genericTest;
    }
    public void setGenericTest(T genericTest) {
        this.genericTest = genericTest;
    }
}

3.2 通过继承定义

//不是泛型类:
//1.泛型参数被擦除
public class ExtendsGeneric extends MyGeneric{}
//1.泛型参数传入实参
public class ExtendsGeneric extends MyGeneric<String>{}

//是泛型类
//1.全部保留
public class  ExtendsGeneric<T1,T2> extends MyGeneric<T1,T2>{}
//2.部分保留
public class  ExtendsGeneric<T1> extends MyGeneric<T1,String>{}

3.3 自定义泛型方法

  • 定义:

    /**
     * 注意:
     *      1.格式
     *          [访问权限] <泛型> 返回类型  方法名([泛型标识参数名称]) 抛出的异常
     *          如:public static <E> List<E> copyFromArrayToList(E[] arr) throws Exception()
     *      2.可声明为静态:
     *          因为泛型参数是在调用方法时确定的,并不是在实例化时确定的
     *      3.与类的泛型参数无关,类有没有泛型参数也没关系
     *
     * 建议:
     *      1.泛型要使用就一路都用。要不用就一路都不要用
     * @param <T>
     */
    public class MyGeneric<T> {
        public <T> List<T> copyFromArrayToList(T[] arr){
            ArrayList<T> list=new ArrayList<>();
            for (T t : arr) {
                list.add(t);
            }
            return list;
        }
    }
    
  • 使用

    public class DefineTest {
        @Test
        public void test3(){
            MyGeneric<String> myGeneric = new MyGeneric<>();
            Integer[] arr = new Integer[]{1,2,3,4};
            //在方法调用时指明泛型参数的类型。
            List<Integer> list = myGeneric.copyFromArrayToList(arr);
        }
    }
    

3.4 使用情境

public class DAO<T> { //表的共性操作的DAO

    //添加一条记录
    public void add(T t){

    }
    //删除一条记录
    public boolean remove(int index){

        return false;
    }
    //修改一条记录
    public void update(int index,T t){

    }
    //查询一条记录
    public T getIndex(int index){

        return null;
    }
    //查询多条记录
    public List<T> getForList(int index){

        return null;
    }

    //泛型方法
    //举例:获取表中一共有多少条记录?获取最大的员工入职时间?
    public <E> E getValue(){

        return null;
    }

}

四、使用泛型后的继承关系

/**
 *   泛型在继承方面的体现
 *		1.虽然类A是类B的父类,但是G<A> 和G<B>二者不具备子父类关系,二者是并列关系。
 *		2.类A是类B的父类,A<G> 是 B<G> 的父类
 * 
 */
public class Explore {
    //1.虽然类A是类B的父类,但是G<A> 和G<B>二者不具备子父类关系,二者是并列关系。
    @Test
    public void test1(){
        List<Object> obj=null;
        ArrayList<String> str=null;
        //obj=str; 编译不通过
    }
    //2.类A是类B的父类,A<G> 是 B<G> 的父类
    @Test
    public void test2(){
        List<String> list=null;
        AbstractList<String> abstractList=null;
        ArrayList<String> arrayList=null;
        list=abstractList;
        abstractList=arrayList;
    }
}

思考:

  • 泛型不一样的类属于平级类,不能相互赋值

  • 那么方法中泛型变化的情况下需要赋值怎么办?

    List<多变> list=null;	//list可以赋值给谁?应该用哪个类型来接收
    

五、通配符

5.1 <?>

/**
 *   通配符:List<?>
 *		1.是List、List等各种泛型的父类
 *		    - 可以用来接收list<泛型多变>的引用。
 *		2.读取:可以
 *	    	- 因为<?>的元素相当于(-∞,+∞),即get()返回值是一个未知的类型。
 *	    	- 但是我们知道,它总是一个Object。
 *	    	- 即 Object=(-∞,+∞)
 *	    3.写入:只能写入null/泛型类型声明的引用
 *	        - 因为<?>的元素相当于(-∞,+∞)。即set(,?)接收的参数是一个未知类型。
 *	        - 我们找不到它的子类。
 *	        - 即 (-∞,+∞)= null/没有其他子类
 *
 */
public class Explore {
    @Test
    public void test3(){
        List<Object> obj=null;
        ArrayList<String> str=null;
        //1.是List、List等各种泛型的父类
        List<?> list=null;
        list=obj;
        list=str;
    }
    @Test
    public <T> void test4(ArrayList<T> t){
        List<?> list=t;
        //2.读取可以:get()返回值是一个未知的类型,但是我们知道,它总是一个Object。即 Object=(-∞,+∞)
        Object o=list.get(0);
        //3.只能写入null:set(,?)接收的参数是一个未知类型,我们找不到它的子类。即 (-∞,+∞)= null/没有其他子类 
        list.set(0,null);
    }
}

5.2 <?>使用要求

  • 只能用在变量声明处

    ArrayList<?> list=null;
    
  • 不能用于泛型类的声明上

    // public class ExploreDemo<?> {}	编译错误
    
  • 不能用于泛型方法的声明上

    // public <?> void  demo1() {}	编译错误
    
  • 不能用于泛型对象的创建上

    // ArrayList<?> list=new ArrayList<?>(); 编译错误
    

5.3 通配符的限制条件

  • extends
    • <? extends Person>:(子类,Person]
    • <T extends Person>:可用于泛型结构的声明
  • super
    • <? super Person>:[Person,父类)
    • <T super Person>:可用于泛型结构的声明
/**
 *	1.<? extends Person>:(子类,Person]
 *	2.<? super Person>:[Person,父类)
 *
 */
public class Explore {
    //1.<? extends Person>:(子类,Person]
    @Test
    public  void test5(){
        ArrayList<? extends Person> extend=null;
        extend=new ArrayList<Person>();
        extend=new ArrayList<Children>();
        Object o=extend.get(0);
        Person person=extend.get(0);
        //Children children=extend.get(0);
        extend.set(0,null);
        //extend.set(0,new Children());
    }
    //2.<? super Person>:[Person,父类)
    @Test
    public  void test6(){
        ArrayList<? super Person> supe=null;
        supe=new ArrayList<Person>();
        supe=new ArrayList<Object>();
        Object o=supe.get(0);
        //Person person=supe.get(0);
        supe.set(0,null);
        supe.set(0,new Person());
    }
}

六、应用举例

6.1、泛型嵌套

参考 2.1 :map.entrySet()返回的类型

6.2、实际案例

在这里插入图片描述

public class Person<T extends Info> {
    T info;
}

interface Info {
}
class Contact implements Info{
}
class Introduction implements Info{

}

七、练习

在这里插入图片描述

  • Dao

    public class Dao <T>{
        private HashMap<String,T> maps;
        public Dao(){
            this.maps=new HashMap<>();
        }
        //1.保存T类型的对象
        public boolean saveT(String id,T t){
            try {
                maps.put(id,t);
            } catch (Exception e) {
                return false;
            }
            return true;
        }
        //2.通过id获取T类型的对象
        public T getT(String id){
            T t = maps.get(id);
            return t;
        }
        //3.更改id对应的T类型的对象
        public T updateT(String id,T t){
            return maps.replace(id, t);
        }
        //4.返回map中所有的T类型的对象
        public List<T> getTs(){
            ArrayList<T> list=new ArrayList<>();
            Set<String> keySets = maps.keySet();
            keySets.forEach(item->list.add(maps.get(item)));
            return list;
        }
        //5删除指定id的对象
        public T deleteT(String id) {
            return maps.remove(id);
        }
    }
    
  • User

    public class User {
        private int id;
        private int age;
        private String name;
    
        public User() {
        }
        public User(int id, int age, String name) {
            this.id = id;
            this.age = age;
            this.name = name;
        }
        @Override
        public String toString() {
            return "User{" +
                    "id=" + id +
                    ", age=" + age +
                    ", name='" + name + '\'' +
                    '}';
        }
    }
    
  • Test:测试

    public class Test {
        public static void main(String[] args) {
            Dao<User> dao=new Dao<>();
            //1.
            dao.saveT("1",new User(1,1,"1"));
            dao.saveT("2",new User(2,2,"2"));
            dao.saveT("3",new User(3,3,"3"));
            //2.
            dao.updateT("3",new User(3,4,"4"));
            //3
            User t = dao.getT("3");
            System.out.println(t);
            //4.
            dao.deleteT("4");
            //5.
            List<User> ts = dao.getTs();
            ts.forEach(System.out::println);
        }
    }
    
    User{id=3, age=4, name='4'}
    User{id=1, age=1, name='1'}
    User{id=2, age=2, name='2'}
    User{id=3, age=4, name='4'}
    
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

愿你满腹经纶

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值