跳到主要内容

第17章-1-泛型

概述

  • 泛型(Generic),参数化类型

  • 传统的方法调用时,传递的参数都是固定类型的,可以是基本类型或引用类型

  • 使用泛型后,传递的参数类型可以不是固定的某种类型,只能是引用数据类型

  • 语法上,使用<类型>作为泛型的语法词,其中类型多以一个单独大写字母表示,如T、E、K、V等

  • 泛型在Java中,尤其是集合类中,应用非常广泛

  • 擦除和补偿

    • 泛型的出现提高了编译时的安全性,正因为编译时对添加的数据做了检查,则程序运行时才不会抛出类型转换异常。因此泛型本质上是编译时期的技术,是专门给编译器用的
    • 加载类的时候,会将泛型擦除掉(擦除之后的类型为Object类型),这个称为泛型擦除法
    • 为什么要有泛型擦除呢?其本质是为了让JDK1.4和JDK1.5能够兼容同一个类加载器。在JDK1.5版本中,程序编译时期会对集合添加的元素进行安全检查,如果检查完是安全的、没有错误的,那么就意味着添加的元素都属于同一种数据类型,则加载类时就可以把这个泛型擦除掉,将泛型擦除后的类型就是Object类,这样擦除之后的代码就与JDK1.4的代码一致
    • 加载类的时候,会默认将类中的泛型擦除为Object类型,所以添加的元素就被转化为Object类型,同时取出的元素也默认为Object类型。而我们获得集合中的元素时,按理说取出的元素应该是Object类型,为什么取出的元素却是实际添加的元素类型呢?
    • 这里又做了一个默认的操作,我们称之为泛型的补偿。在程序运行时,通过获取元素的实际类型进行强转,这就叫做泛型补偿(不必手动实现强制转换)。获得集合中的元素时,虚拟机会根据获得元素的实际类型进行向下转型,也就是会恢复获得元素的实际类型,因此我们就无需手动执行向下转型操作,从本质上避免了抛出类型转换异常
  • 典型应用场景

    • 如当前需要一个存储Integer类型数据的集合类,除了使用数组,可定义一个IntegerArrayList集合类,里面包装一个数组来实现

    • 过一阵,需要一个存储Float类型数据的集合类,则需要重新定义一个FloatArrayList集合类

    • 而且,可以陆续还有别的类型,如Student等自定义类型,那需要定义更多的集合类

    • 此时,使用泛型,就可以针对上述多种类型,只定义一个泛型的ArrayList集合类

    • 应用场景实例

      • 应用实例1,传统方式定义的集合类,不推荐

        • 集合类,只支持Integer类型

          package com.bjpowernode.demo;

          /**
          * Integer类型集合类
          */
          public class IntegerArrayList {
          //保存元素的数组
          private Integer[] datas;
          //当前下标
          private int index;

          /**
          * 无参构造,默认长度为10
          */
          public IntegerArrayList() {
          this(10);
          }

          /**
          * 有参构造,初始化指定长度数组
          *
          * @param capacity
          */
          public IntegerArrayList(int capacity) {
          datas = new Integer[capacity];
          }

          /**
          * 添加元素
          *
          * @param data
          */
          public void add(Integer data) {
          this.datas[this.index] = data;

          this.index++;
          }

          /**
          * 获取指定下标元素
          * @param index
          * @return
          */
          public Integer get(int index) {
          return this.datas[index];
          }

          /**
          * 获取长度
          *
          * @return
          */
          public int getLength() {
          return this.index + 1;
          }
          }
        • 集合类测试

          package com.bjpowernode.demo;

          import java.util.ArrayList;

          /**
          * Integer类型的集合类测试,【只支持Integer类型】
          */
          public class IntegerArrayListTest {
          public static void main(String[] args) {
          //定义Integer集合
          IntegerArrayList integerArrayList = new IntegerArrayList();

          //添加元素,只支持Integer类型
          integerArrayList.add(3);
          integerArrayList.add(5);
          integerArrayList.add(7);

          //获取元素
          Integer result = integerArrayList.get(1);
          System.out.println(result);

          java.util.ArrayList a = new ArrayList();
          }
          }
  • 应用实例2,泛型方式的集合类,推荐

    • 集合类,支持所有引用类型

          package com.bjpowernode.demo;

      /**
      * 通用集合类,使用泛型
      */
      public class ArrayList<E> {
      //保存元素的数组
      private Object[] datas;
      //当前下标
      private int index;

      /**
      * 无参构造,默认容量为10
      */
      public ArrayList() {
      this(10);
      }

      /**
      * 有参构造,指定容量
      *
      * @param capacity
      */
      public ArrayList(int capacity) {
      datas = new Object[capacity];
      }

      /**
      * 添加元素
      *
      * @param data
      */
      public void add(E data) {
      this.datas[this.index] = data;

      this.index++;
      }

      /**
      * 获取指定下标元素
      *
      * @param index
      * @return
      */
      public E get(int index) {
      return (E) this.datas[index];
      }

      /**
      * 获取长度
      *
      * @return
      */
      public int getLength() {
      return this.index + 1;
      }
      }
    • 集合类测试

          package com.bjpowernode.demo;

      /**
      * 泛型的集合类测试,【支持所有类型】
      */
      public class ArrayListTest {
      public static void main(String[] args) {
      System.out.println("--------------泛型集合-Integer--------------");
      //定义泛型集合,支持Integer
      com.bjpowernode.demo.ArrayList<Integer> integerArrayList = new ArrayList<>();

      //添加元素,只支持Integer类型
      integerArrayList.add(3);
      integerArrayList.add(5);
      integerArrayList.add(7);
      // integerArrayList.add(44.55); //报错,当前泛型对象只支持Integer

      //获取元素
      Integer integerResult = integerArrayList.get(1);
      System.out.println(integerResult);

      System.out.println("--------------泛型集合-Double--------------");
      //定义泛型集合,支持Double
      com.bjpowernode.demo.ArrayList<Double> doubleArrayList = new ArrayList <>();

      //添加元素,只支持Double类型
      doubleArrayList.add(1.1);
      doubleArrayList.add(3.3);
      doubleArrayList.add(5.5);
      // doubleArrayList.add(77); //报错,当前泛型对象只支持Double

      //获取元素
      Double doubleResult = doubleArrayList.get(1);
      System.out.println(doubleResult);

      System.out.println("--------------泛型集合-其他类型...--------------");
      }
      }

泛型的定义与使用

  • 泛型主要分类泛型类、泛型方法、泛形接口

  • Java类库中提供了大量的泛型类,如ArrayList、Map等

  • 同样,也可以对泛型进行自定义

  • 自定义泛型

    • 泛型类

      • 定义泛型类语法规则

        访问修饰符 [其他修饰符] class 类名<泛型1, 泛型2, 泛型3, ...> {

        }
      • 参考上述ArrayList,就是典型的自定义 泛型类的使用

    • 泛型方法

      • 定义泛型方法语法规则

        访问修饰符 [其他修饰符] <泛型1, 泛型2, 泛型3, ...> 返回值类型 方法名(形参列表) {

        }
      • 可以直接针对方法进行泛型定义

      • 应用实例

        • 应用实例1,泛型方法的定义和使用

          • 泛型方法,冒泡排序

            package com.bjpowernode.demo;

            /**
            * 泛型冒泡排序类
            */
            public class BubbleSortUtil {
            /**
            * 泛形冒泡排序,支持任何实现了Comparable接口的元素数组
            */
            public static <T extends Comparable> void sort(T[] datas) {
            //外层循环,表示比较的轮次
            for (int i = 0; i < datas.length; i++) {
            //内层循环,每轮对数据进行比较,将大的(重的)沉下去
            for (int j = 0; j < datas.length - (i + 1); j++) {
            //如果当前位置的值大于后面位置的值,则替换,即往下沉
            if (datas[j].compareTo(datas[j + 1]) > 0) {
            T temp = datas[j + 1];
            datas[j + 1] = datas[j];
            datas[j] = temp;
            }
            }
            }
            }
            }
          • 泛型方法测试

            package com.bjpowernode.demo;

            import java.util.Arrays;

            public class BubbleSortUtilTest {
            public static void main(String[] args) {
            System.out.println("---------------Integer类型排序---------------");
            Integer[] integerDatas = {33, 155, 77, 22, 66, 44, 111, 222, 133, 144};
            System.out.println("【排序前】:" + Arrays.toString(integerDatas));
            //排序
            BubbleSortUtil.sort(integerDatas);
            System.out.println("【排序后】:" + Arrays.toString(integerDatas));

            System.out.println("---------------Double类型排序---------------");
            Double[] doubleDatas = {99.99, 8.88, 33.33, 44.44, 55.55, 111.11, 77.77};
            System.out.println("【排序前】:" + Arrays.toString(doubleDatas));
            //排序
            BubbleSortUtil.sort(doubleDatas);
            System.out.println("【排序后】:" + Arrays.toString(doubleDatas));

            System.out.println("---------------其他类型排序...元素需要实现Comparable接口---------------");
            }
            }
    • 泛型接口

      • 定义泛型接口语法规则

        访问修饰符 [其他修饰符] interface 接口名<泛型1, 泛型2, 泛型3, ...> {

        }
      • 实现接口的子类,可以是普通类,也可以是泛型类

      • 应用实例

        • 应用实例1,子类是普通类

          • 泛型接口,获取最大值

            package com.bjpowernode.demo;

            /**
            * 操作接口
            */
            public interface IOperation<T> {
            /**
            * 最大值
            */
            T max(T one, T two);
            }
          • 普通子类,实现接口,支持Integer

            package com.bjpowernode.demo;

            /**
            * Integer操作类
            */
            public class IntegerOperate implements IOperation <Integer> {
            @Override
            public Integer max(Integer one, Integer two) {
            return one > two ? one : two;
            }
            }
          • 普通子类,实现接口,支持Employee

            Employee类

            package com.bjpowernode.demo;

            /**
            * 员工类
            */
            public class Employee {
            private Integer id;
            private String name;
            private Character sex;
            private Integer age;
            private Boolean isLeader;

            public Employee() {
            }

            public Employee(Integer id, String name, Character sex, Integer age, Boolean isLeader) {
            this.id = id;
            this.name = name;
            this.sex = sex;
            this.age = age;
            this.isLeader = isLeader;
            }

            public Integer getId() {
            return id;
            }

            public void setId(Integer id) {
            this.id = id;
            }

            public String getName() {
            return name;
            }

            public void setName(String name) {
            this.name = name;
            }

            public Character getSex() {
            return sex;
            }

            public void setSex(Character sex) {
            this.sex = sex;
            }

            public Integer getAge() {
            return age;
            }

            public void setAge(Integer age) {
            this.age = age;
            }

            public Boolean getLeader() {
            return isLeader;
            }

            public void setLeader(Boolean leader) {
            isLeader = leader;
            }

            @Override
            public String toString() {
            return "Employee{" + "id=" + id + ", name='" + name + '\'' + ", sex=" + sex + ", age=" + age + ", isLeader=" + isLeader + '}';
            }
            }

            普通子类

            package com.bjpowernode.demo;

            /**
            * 员工操作类
            */
            public class EmployeeOperate implements IOperation <Employee> {
            @Override
            public Employee max(Employee one, Employee two) {
            return one.getAge() > two.getAge() ? one : two;
            }
            }
          • 普通子类测试类

            package com.bjpowernode.demo;

            public class OperationTest {
            public static void main(String[] args) {
            System.out.println("---------------Integer的最大值---------------");
            Integer one = 33;
            Integer two = 55;
            IOperation<Integer> integerOperation = new IntegerOperate();
            Integer integerMax = integerOperation.max(one,two);
            System.out.println(integerMax);

            System.out.println("---------------Employee的最大值---------------");
            //员工1
            Employee e1 = new Employee(11, "张三", '男', 25, true);
            //员工2
            Employee e2 = new Employee(13, "李四", '女', 27, false);
            IOperation<Employee> employeeOperation = new EmployeeOperate();
            Employee employeeMax = employeeOperation.max(e1,e2);
            System.out.println(employeeMax);
            }
            }
        • 应用实例2,子类是泛型类

          • 泛型接口,获取最大值

            package com.bjpowernode.demo;

            /**
            * 操作接口
            */
            public interface IOperation<T> {
            /**
            * 最大值
            */
            T max(T one, T two);
            }
          • 泛型子类,针对实现Comparable接口元素的数组

            package com.bjpowernode.demo;

            import java.util.Comparator;

            /**
            * 泛型操作类,使用了Comparable接口,限定E的类型必须实现Comparable接口
            */
            public class GenericOperation<E extends Comparable> implements IOperation <E> {
            @Override
            public E max(E one, E two) {
            return one.compareTo(two) > 0 ? one : two;
            }
            }
          • 泛型子类测试类

            odpackage com.bjpowernode.demo;

            public class GenericOperationTest {
            public static void main(String[] args) {
            /*
            注意:包装类都实现了Comparable接口
            */
            System.out.println("---------------Integer的最大值---------------");
            Integer one = 33;
            Integer two = 55;
            IOperation <Integer> integerOperation = new GenericOperation <>();
            Integer integerMax = integerOperation.max(one, two);
            System.out.println(integerMax);

            System.out.println("---------------Double的最大值---------------");
            Double first = 77.77;
            Double second = 99.99;
            IOperation <Double> doubleOperation = new GenericOperation <>();
            Double doubleMax = doubleOperation.max(first, second);
            System.out.println(doubleMax);
            }
            }

【练习】

  1. 练习应用实例内容,完成代码编写

边界【扩展】

  • 泛型定义时,也可以对其适用的类型做一定的限制
  • 主要通过extends和super两个关键字实现
  • extends
    • 表示泛型只能是指定类型或其后代类型,俗称上界
    • 语法格式
      • 用于泛型定义类型限定:<T extends 类型>,如:<T extends Comparable>限制泛型T只能是Comparable类型或其后代类型
      • 用于上界通配符,与通配符?结合使用
  • super
    • 表示泛型只能是指定类型或其上辈类型,俗称下界
    • 语法格式: <? super 类型>,与通配符?结合使用

通配符【扩展,了解】

  • 使用?语法,表示任意泛型

  • 并可与边界组合

  • 分别为

    • 无界通配符,格式为<?>,表示可以是任意泛型
    • 上界通配符,格式为<? extends 类型>,表示只能是指定类型或其后代类
    • 下界通配符,格式为<? super 类型>,表示只能是指定类型或其上前辈类
  • 应用实例

    • 具备继承关系的类

      动物类

      package com.bjpowernode.demo;

      /**
      * 动物类
      */
      public class Animal {
      private String name;

      public String getName() {
      return name;
      }

      public void setName(String name) {
      this.name = name;
      }

      @Override
      public String toString() {
      return "Animal{" + "name='" + name + '\'' + '}';
      }
      }

      鸟类,动物类子类

      package com.bjpowernode.demo;
      /**
      * 鸟类
      */
      public class Bird extends Animal{
      @Override
      public String toString() {
      return "Bird{} " + super.toString();
      }
      }

      狗类,动物类子类

      package com.bjpowernode.demo;

      /**
      * 狗类
      */
      public class Dog extends Animal {
      @Override
      public String toString() {
      return "Dog{} " + super.toString();
      }
      }

      哈士奇类,狗类子类

      package com.bjpowernode.demo;

      /**
      * 哈士奇类
      */
      public class Husky extends Dog{
      @Override
      public String toString() {
      return "Husky{} " + super.toString();
      }
      }

      泰迪类,狗类子类

      package com.bjpowernode.demo;

      /**
      * 泰迪类
      */
      public class Teddy extends Dog{
      @Override
      public String toString() {
      return "Teddy{} " + super.toString();
      }
      }
    • 自定义集合类

      package com.bjpowernode.demo;

      /**
      * 通用集合类,使用泛型
      */
      public class ArrayList<E> {
      //保存元素的数组
      private Object[] datas;
      //当前下标
      private int index;

      /**
      * 无参构造,默认容量为10
      */
      public ArrayList() {
      this(10);
      }

      /**
      * 有参构造,指定容量
      *
      * @param capacity
      */
      public ArrayList(int capacity) {
      datas = new Object[capacity];
      }

      /**
      * 添加元素
      *
      * @param data
      */
      public void add(E data) {
      this.datas[this.index] = data;

      this.index++;
      }

      /**
      * 获取指定下标元素
      *
      * @param index
      * @return
      */
      public E get(int index) {
      return (E) this.datas[index];
      }

      /**
      * 获取长度
      *
      * @return
      */
      public int getLength() {
      return this.index + 1;
      }
      }
    • 通配符测试类

      package com.bjpowernode.demo;

      /**
      * 通配符使用示例
      */
      public class WildcardDemo {
      public static void main(String[] args) {
      //动物列表
      com.bjpowernode.demo.ArrayList<Animal> animals = new ArrayList <>();
      Dog dog = new Dog();
      dog.setName("小花");
      animals.add(dog);
      Bird bird= new Bird();
      bird.setName("八哥");
      animals.add(bird);

      //狗列表
      com.bjpowernode.demo.ArrayList<Dog> dogs = new ArrayList <>();
      dog = new Dog();
      dog.setName("旺财");
      dogs.add(dog);
      Husky husky = new Husky();
      husky.setName("二货");
      dogs.add(husky);

      //鸟列表
      com.bjpowernode.demo.ArrayList<Bird> birds = new ArrayList <>();
      bird= new Bird();
      bird.setName("乌鸦");
      birds.add(bird);
      bird = new Bird();
      bird.setName("喜鹊");
      birds.add(bird);

      //使用无界通配符
      System.out.println("-------------------无界通配符-------------------");
      WildcardDemo.showDatas(animals);
      System.out.println("================");
      WildcardDemo.showDatas(dogs);
      System.out.println("================");
      WildcardDemo.showDatas(birds);

      //使用上界通配符
      System.out.println("-------------------上界通配符-------------------");
      // WildcardDemo.showDatasForExtends(animals); //报错,上界限定为Dog
      WildcardDemo.showDatasForExtends(dogs);
      // WildcardDemo.showDatasForExtends(birds); //报错,上界限定为Dog

      //使用下界通配符
      System.out.println("-------------------下界通配符-------------------");
      WildcardDemo.showDatasForSuper(dogs);
      System.out.println("================");
      WildcardDemo.showDatasForSuper(animals);
      // WildcardDemo.showDatasForSuper(bird); //报错,下界限定为Dog
      }

      /**
      * 无限通配符
      * @param datas
      */
      public static void showDatas(ArrayList <?> datas) {
      for (int index = 0; index < datas.getLength()-1; index++) {
      System.out.println(datas.get(index));
      }
      }

      /**
      * 上界通配符
      * @param datas
      */
      public static void showDatasForExtends(ArrayList <? extends Dog> datas) {
      for (int index = 0; index < datas.getLength()-1; index++) {
      System.out.println(datas.get(index));
      }
      }

      /**
      * 下界通配符
      * @param datas
      */
      public static void showDatasForSuper(ArrayList <? super Dog> datas) {
      for (int index = 0; index < datas.getLength()-1; index++) {
      System.out.println(datas.get(index));
      }
      }
      }

【练习】

  1. 练习应用实例内容,完成代码编写

实战和作业

  1. 【扩展】编写程序,要求如下,
    1. 模拟java.util.Stack类,实现一个自定义栈,能存放任意类型的数据,并提供入栈、出栈、栈中元素个数等方法
    2. 对自定义栈进行测试,分别使用一个包装类和一种自定义类型测试