Java六十三: 泛型

本文详细介绍了Java中的泛型,包括在集合接口中的应用,如何自定义泛型类和方法,以及泛型在继承关系下的表现。泛型提高了代码的类型安全性和效率,避免了类型转换的麻烦。同时,文章探讨了通配符的使用,如<?> extends A和<?> super B,以及它们在读取和写入数据时的限制。通过对泛型深入理解,开发者可以更好地设计和实现数据结构与算法。
摘要由CSDN通过智能技术生成

泛型

导读
  1. 泛型在各集合接口中的应用
  2. 自定义类和自定义泛型方法需注意的点,静态泛型方法内部不可以使用类的泛型(生命周期不同),非静态泛型方法的内部才可以使用类的泛型
  3. 继承关系下,子类对于父类泛型的几种继承情况
  4. 通配符是重点,特别是两种有线通配符**<? extends A> / < ? super B>**
引用
  • 标签

  • 设计背景

    • 集合容器类在设计阶段/声明阶段不能确定这个容器到底实际存的是什么类型的对象,所以在jdk5之前只能把元素类型设计为Object;

    • jdk5之后使用泛型来解决:因为这个时候除了元素的类型不确定,其他部分是确定的,例如关于这个元素如何保存,如何管理等,因此此时把元素的类型设计成一个参数,这个类型参数叫做泛型。

    • Collection< E> ,List< E>,ArrayList 这个< E>就是类型参数,就是泛型

一、集合中的泛型
  1. 在集合中使用泛型

    ① 集合接口或集合类在jdk5.0时都改为带泛型的结构
    ② 在实例化集合类时,可以指明具体的泛型类型
    ③指明完后,在集合类或接口中凡是定义类或接口时,内部结构使用到类的泛型位置,都被指定为实例化的泛型类型。
    ④ 泛型的类型必须是类,不能是基本数据类型。需要用到基本数据类型的位置,拿包装类替代
    ⑤ 如果实例化时,没有指明泛型的类型,默认类型为 java.lang.Object

  2. 代码示例

    @Test
    	// 泛型在List接口中的使用
        public void test1() {
            // 实例化对象时指定具体的泛型类型为Integer
            ArrayList<Integer> list = new ArrayList<>();
            list.add(123);
            list.add(346);        
            list.add(3456);
            // 内部结构使用到类的泛型的位置,都与在实例化时指定的泛型类型一致
            // Iterator本身就是一个带泛型的接口,所以被对象调用时也被指定
            Iterator<Integer> iterator = list.iterator();
            while (iterator.hasNext()) {
                System.out.println(iterator.next());
            }
        }
        
    @Test
    	// 泛型在Map接口中的使用
        public void test2() {
            Map<String, Integer> map = new HashMap<>(5);
            map.put("Tom",59);
            map.put("Susan",19);
            map.put("Jock",29);
            // 泛型的嵌套
            Set<Map.Entry<String,Integer>> set = map.entrySet();
            for (Map.Entry<String,Integer> entry : set) {
                System.out.println(entry);
            }
    
    		// entrySet()方法的源码      
            transient Set<Map.Entry<K,V>> entrySet;
    		
            public Set<Map.Entry<K,V>> entrySet() {
                Set<Map.Entry<K,V>> es;
                return (es = entrySet) == null ? (entrySet = new HashMap.EntrySet()) : es;
            }
        }    
    
二、自定义泛型类/接口、自定义泛型方法
  1. 自定义泛型类

    ① 泛型类可能有多个参数,此时应将多个参数一起放在尖括号内。例如: <E1,E2,E3>

    ② 泛型类的构造器不需要加泛型

    	public GenericsClass() {}
    

    ③ 实例化后,操作原来泛型位置的结构必须与指定的泛型类型一致

    ④ 泛型不同的引用不能互相赋值

    泛型如果不指定,将被擦除,泛型对应的类型均按照Object处理,但不等价于Object。

    经验:泛型要使用一路都用。要不用,一路都不用

    ⑥ 如果泛型类是一个接口或抽象类,则不可创建泛型类的对象

    -⑦ jdk7之后,泛型操作简单化:

    	ArrayList<Fruit> flist = new ArrayList<>();
    

    ⑧ 在类/接口上声明的泛型,在本类或本接口中即代表某种类型,可以作为非静态属性的类型、非静态方法的参数类型、非静态方法的返回值类型。但:非泛型类的静态方法中不能使用泛型,因为泛型在实例化时才会被指定,静态是随着类的加载而加载的,生命周期不同。

    异常类不能是泛型

    ⑩ 泛型的数组的声明方式

    	// 实际上就是造了一个Object类的数组,然后强制转换成T[]类型
    	T[] arr = (T[]) new Object[capacity];
    

    ⑾ 自定义泛型类代码示例

    	public class GenericsClassTest<T> {
    	    private String name;
    	    private int age;
    	    // 1.带泛型的属性
    	    private  T orderT;
    	    public GenericsClassTest() {}
    	    // 2.带泛型参数的构造器
    	    public GenericsClassTest(String name, int age, T orderT) {
    	        this.name = name;
    	        this.age = age;
    	        this.orderT = orderT;
    	    }
    	    // 3.带泛型的非静态方法
    	    public T getOrderT() {
    	        return orderT;
    	    }
    	    public void setOrderT(T orderT) {
    	        this.orderT = orderT;
    	    }
    	}
    
  2. 自定义泛型类的子类

    ① 示例泛型父类如下:

    	class Father<T,E> {
    		//fields;constructor;getter/setter;methods···
    	}
    

    ② 子类继承了父类的泛型

    	class Sub<T,E> extends Father<T,E> {}
    

    ③ 子类部分保留了父类的泛型

    	class Sub<String,E> extends Father<T,E> {}
    

    ④ 子类自定义了泛型,擦除了父类的泛型

    	class Sub<A,B> extends Father {}
    

    ⑤ 父类的泛型擦除了

    	class Sub extends Father {}
    

    ⑥ 父类的泛型指定了具体类型

    	class Sub extends Father<String,Integer> {}
    
  3. 自定义泛型方法

    泛型方法中的泛型结构、泛型参数可以与类的泛型参数没有任何关系

    方法可不可以是泛型结构,与所在的类是不是泛型没有关系

    泛型方法可以是静态的,因为方法的泛型在被调用时才会确定类型,与static不冲突,**但是其本身及内部结构不允许使用所在类的泛型,**因为类的泛型需要实例化的时候才能被确定。

    如果静态泛型方法使用了类的泛型,那么类本身在调用静态方法时,该静态方法的泛型应该是什么类型呢?所以冲突了;如果静态泛型方法没有使用类的泛型,那么它在被类调用时,就可以马上指定泛型的具体类型

    在泛型方法的头部的返回值类型/void前面应该加上 ,表明 E 代表一个泛型类型,而不是某个类名为E的类

    • 代码示例

      public class GenericsTest<T> {
          // 泛型方法测试
          public static void main(String[] args) {
              GenericsTest<String> gen = new GenericsTest<>();
              Integer[] arr = {1, 2, 3, 4, 5};
              List<Integer> list = gen.fromArrayToList(arr);
              System.out.println(list);
          }
          // 泛型方法的创建
          public static <E> List<E> fromArrayToList(E[] arr1) {
              ArrayList<E> list1 = new ArrayList<>();
              for (E e : arr1){
                  list1.add(e);
              }
              return list1;
          }
      }
      
  4. 自定义泛型类和泛型方法的应用 - - 对于数据库的操作

    ① 表的共性操作DAO

    	class DAO<T> {
    	    public void add(T t) {
    	
    	    }
    	    // 未确定T的类型,先返回null
    	    public List<T> getForList(int index) {
    	        return null;
    	    }
    	    
    	    // 泛型方法
    	    public <E> E getValue() {
    	        return null;
    	    }
    	}
    

    ② 数据库中的一张表对应一个类 - - 例如:Customer类

    	class Customer {
    	
    	}
    

    ③ 针对某张表的DAO

    	class CustomerDAO<T> extends DAO<T> {
    	    
    	}
    

    ④ 操作示例

    	public class ExtendsTest {
    	    public static void main(String[] args) {
    	        // 确定了CustomerCao泛型的类型为:Customer
    	        CustomerCao<Customer> cao1 = new CustomerCao<>();
    	        Customer c1 = new Customer();
    	        // 通过该对象调的父类中的泛型结构都必须时Customer类型的
    	        cao1.add(c1);
    	        // 调用泛型方法
    	        Long num = cao1.<Long>getValue();
    	        String str = cao1.<String>getValue();
    	    }
    	}
    
三、泛型在继承方面的体现 及 通配符 < ?>
  1. 类A是类B是父类,但是G不是G 的父类,是并列关系,没有继承关系

    类A是类B是父类,A 仍然是B 的父类

    ① 代码示例

    	@Test
    	    public void test1() {
    	        
    			// 字符串是不可改变的,当有新值赋给obj1时,会指向其它内存空间
    	        Object obj1 = null;
    	        String str1 = "aa";
    	        obj1 = str1;
    	        System.out.println(obj1);
    	        
    			// 数组也具有不可变性
    	        Object[] obj2 = null;
    	        String[] str2 = {"AA","BB"};
    	        obj2 = str2;
    	        for (Object o : obj2) {
    	            System.out.println(o);
    	        }
    	        
    	        // List集合指定泛型类型后,也和数组类似,
    	        List<Object> obj = null;
    	        List<String> str = null;
    	        // 下式不成立,详见内存解析图
    	        // obj = str;
    	
    	    }
    

    ② 内存图解释

    在这里插入图片描述

  2. 通配符

    类A是类B的父类,G 和 G 是并列关系,G<?> 是它们共同的父类

    @Test
        public void test2() {
            List<Object> obj = null;
            List<String> str = null;
            List<?> list = null;
            list = obj;
            list = str;        
        }
    
  3. 通配符的使用

    用于读取数据,不允许添加数据,因为作为父类,没有指定具体类型,会造成指定集合内数据类型冲突

    @Test
        public void test2() {
            List<?> list = null;
            // 读取数据
            ArrayList<String> list3 = new ArrayList<>();
            list3.add("AA");
            list3.add("BB");
            list3.add("CC");
            list = list3;
    
            for (Object o : list) {
                System.out.println(o);
            }
        }
    
  4. 有限通配符

    < ? extends A >

    泛型类型是A本身或A的子类,只能读取,不能写入

    < ? super A >

    泛型类型是A本身或A的父类,可以写入A本身或A的子类,多态的体现

        @Test
        public void test4() {
            List<Object> obj = null;
            List<DAO> dao = null;
            List<CustomerDao> cus = null;
    
            // ? extends 相当于 (-∞ ,DAO)
            List<? extends DAO> list1 = null;
            list1 = cus;
            list1 = dao;
    
            // ? super  相当于 (DAO ,+∞)
            List<? super DAO> list2 = null;
            list2 = dao;
            list2 = obj;
    
        }
    }
    class DAO {}
    class CustomerDao extends DAO {}
    
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

e_nanxu

感恩每一份鼓励-相逢何必曾相识

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

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

打赏作者

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

抵扣说明:

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

余额充值