JAVA高级复习-泛型

泛型

1、定义

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

2、概念

所谓泛型,即允许在定义类、接口时,通过一个标识表示类中某个属性的类型或者是某个方法的返回值及参数类型。这个类型参数将在使用时确定(例如,继承或实现这个接口;用这个类型声明变量、创建对象时)。

3、参数化类型

JDK1.5后引入的概念,即允许我们在创建集合对象时再指定集合元素的类型,比如List, 实例化集合对象时使用List

4、使用示例

  • 在集合中使用泛型

    1. 集合接口、集合类在jdk5.0时修改为带泛型的结构;
    2. 在实例化集合类时,可以指明具体的泛型类型;
    3. 指明泛型类型后,在集合类、集合接口中,凡是定义类、接口时,内部结构(方法、构造器、属性等)使用到类的泛型的位置,都需要指定为实例化的泛型类型;
    4. 泛型的类型必须是类,不能是基本数据类型,需要用到基本数据类型的位置,使用其包装类。
    5. 如果实例化时没有指明泛型的类型,默认类型为java.lang.Object类型,不建议这样使用,所以实例化时要指明泛型的类型
  • 如何自定义泛型结构

    1. 自定义泛型类、泛型接口

      泛型类不能时静态的,因为静态类是在类实例化之前就初始化了,而此时泛型类型还不确定,初始化失败。

      /**
       * 自定义泛型类
       *
       * @param <T> 自定义时,可以将泛型视为一个具体的类,这样容易理解
       */
      public class Person<T> {
          private String sex;
          private Integer age;
          //类的内部结构就可以使用类的泛型(属性、方法、参数等)
          private T personT;
      
          public Person() {
          }
          public Person(String sex, Integer age, T personT) {
              this.sex = sex;
              this.age = age;
              this.personT = personT;
          }
          public String getSex() {
              return sex;
          }
          public void setSex(String sex) {
              this.sex = sex;
          }
          public Integer getAge() {
              return age;
          }
          public void setAge(Integer age) {
              this.age = age;
          }
          public T getPersonT() {
              return personT;
          }
          public void setPersonT(T personT) {
              this.personT = personT;
          }
          @Override
          public String toString() {
              return "Person{" +
                      "sex='" + sex + '\'' +
                      ", age=" + age +
                      ", personT=" + personT +
                      '}';
          }
      }
      
      /**
       * 泛型子类
       * 继承泛型父类时,父类指定泛型类型,子类实例化时,就不用再指定泛型类型了。
       */
      public class man extends Person<Integer> {
      
      }
      
      /**
       * 泛型子类
       * 继承泛型父类时,父类不指定泛型类型,子类实例化时,需要指定泛型类型了。
       */
      public class woman<T> extends Person<T> {
      
      }
      
    2. 自定义泛型方法

      泛型方法定义:在方法中出现了泛型的结构。注:方法的泛型参数与类的泛型参数没有任何关系。

      // 自定义泛型方法
      public <E> List<E> copyFromArrayToList(E[] arr) {
      	List<E> list = new ArrayList<>();
          for (E e : list) {
              list.add(e);
          }
      	return list;
      }
      

      如何理解方法的返回值List前面还增加了一个

      答:告诉编译器E是一个泛型,不是一个参数类型的类,如果泛型方法前不增加,编译器会认为E是一个参数类型的类,就会去找这个类,此时是找不到的,所以编译器会提示错误。

      // 错误的泛型方法写法(返回值前没有加泛型)
      public List<E> copyFromArrayToList(E[] arr) {
          List<E> list = new ArrayList<>();
          for (E e : list) {
              list.add(e);
          }
          return list;
      }
      

      静态泛型方法:泛型参数是在调用方法时确定的,并非在实例化类时确定,所以可以有静态泛型方法。

      /**
       * 静态泛型方法
       */
      public static <E> List<E> copyFromArrayToList2(E[] arr) {
          List<E> list = new ArrayList<>();
          for (E e : list) {
              list.add(e);
          }
          return list;
      }
      

5、通配符?

  • A与B是父子类关系,那G<'A'>与G<'B'>是并列关系,非父子类关系,G<?>是他们的根父类
/**
 * 通配符?在集合中的使用示例
 * List<?>是List<Object>与List<String>的父类,而List<Object>与List<String>是并列关系,非父子类关系
 * 结论:A与B是父子类关系,那G<A>与G<B>是并列关系,非父子类关系,G<?>是他们的根父类
 */
public static void main(String[] args) {
    List<Object> list1 = new ArrayList<>(Arrays.asList(1, 2, 3));
    List<String> list2 = new ArrayList<>(Arrays.asList("a", "b", "c"));
    List<?> list = new ArrayList<>(Arrays.asList(1, 2, 3));
    print(list1);
    print(list2);
    print(list);
}

private static void print(List<?> list) {
    Iterator<?> iterator = list.iterator();
    while (iterator.hasNext()) {
        Object obj = iterator.next();
        System.out.println(obj);
    }
}
  • G<?>集合(使用通配符的集合对象)数据的操作

    1. 读取数据操作,返回值为java.lang.Object,因为Object时所有类的父类;
    2. 添加数据操作,不能向G<?>集合中添加数据,除了添加null对象,只能做读取操作
  • 有限制条件的通配符的使用

    /**
     * 有限制条件的通配符的使用
     * 通配符做子类继承时,通配符占位的类型必须是Animal的子类,包含Animal
     * 通配符做超类时,通配符占位的类型必须是Animal的父类,包含Animal
     */
    public static void main(String[] args) {
        List<? extends Animal> list1 = null;
        List<? super Animal> list2 = null;
    
        List<Dog> list3 = new ArrayList<>();
        List<Animal> list4 = new ArrayList<>();
        List<Object> list5 = new ArrayList<>();
    
        list1 = list3;
        list1 = list4;
        //list1 = list5;//报错,不能赋值,因为Object是Animal的父类
    
        //list2 = list3;//报错,不能赋值,因为Dog是Animal的子类
        list2 = list4;
        list2 = list5;
        
        /**
         * 取值操作
         */
        //通配符做子类继承时,集合取值后,返回值不能低于(<=)父类
        Animal animal = list1.get(0);
        Object obj = list1.get(0);
        //通配符做超类时,集合取值后,返回值必须高于父类(>)
        Object object = list2.get(0);
    
        /**
         * 填充值操作
         */
        //list1.add(new Object());//报错,不能填充
        //list1.add(new Animal());//报错,不能填充
        //list1.add(new Dog());//报错,不能填充
    
        //list2.add(new Object());//报错,不能填充
        list2.add(new Animal());
        list2.add(new Dog());
    }
    
posted @ 2022-01-02 13:45  温森  阅读(56)  评论(0编辑  收藏  举报