Back

Java泛型

泛型


  1. 引出泛型
  2. 泛型的介绍及使用
  3. 泛型的使用细节
  4. 泛型课堂练习题
  5. 自定义泛型
  6. Junit

引出泛型

  • 现在有这样的一个需求

    1. 编写一个程序,在ArrayList中,添加三个Dog对象
    2. Dog对象含有name和age,并输出name和age(要求使用getXxx方法)

    用传统的方法编码,代码演示如下

    创建Dog类

    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 void setName(String name) {
            this.name = name;
        }
    
        public int getAge() {
            return age;
        }
    
        public void setAge(int age) {
            this.age = age;
        }
    }
    

    创建测试类Generic01

    package com.generic;
    
    import java.util.ArrayList;
    
    public class Generic01 {
        public static void main(String[] args) {
            ArrayList arrayList = new ArrayList();
            arrayList.add(new Dog("旺财",10));
            arrayList.add(new Dog("小黄",8));
            arrayList.add(new Dog("富贵",5));
    
            //使用增强for遍历
            for (Object o : arrayList) {
                //向下转型
                Dog dog = (Dog) o;
                System.out.println(dog.getName()+"--"+dog.getAge());
            }
    
        }
    }
    

    用传统方式即可很快完成需求。

    假如程序员不小心把一个Cat对象添加到集合里面,会发生什么呢?

    arrayList.add(new Cat("小黑",3));
    

    编译运行,发现程序报错

    image01

    可以发现,倘若我们使用传统的方式来解决需求时,程序会有两个缺点

    1. 不能对加入到集合ArrayList中的数据类型进行约束(不安全,会发生上面那样的编译错误,而且编译器编译时识别不了)
    2. 遍历的时候需要进行类型转换,如果集合中的数据量较大时会对效率有影响

    而我们使用泛型便可以很好的解决上述两个缺点!

    把测试类Generic01中集合的定义改为

    ArrayList<Dog> arrayList = new ArrayList<Dog>();
    

    便是使用了泛型,可以规范ArrayList集合中元素的取值。而且当我们使用泛型之后,如果编译器发现添加的类型不满足集合规定的要求,就会报错,可以避免发生异常。

    image02

    同时,我们遍历集合的时候,不再需要先取出Object类型再进行向下转型了,可以直接获取到Dog类型的数据。

    //使用增强for遍历
    for (Dog dog : arrayList) {
        System.out.println(dog.getName()+"--"+dog.getAge());
    }
    

    使用泛型的好处

    1. 编译时,编译器会检查添加元素的类型,提高了安全性
    2. 减少了类型转换的次数,提高效率

泛型的介绍及语法与使用

  • 泛型的介绍(泛型是可以代表一种数据类型的)

    1. 泛型又称参数化类型,是jdk5.0出现的新特性,解决数据类型的安全性问题

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

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

    4. 泛型的作用是:可以在类声明时通过一个标识表示类中的某个属性的类型,或者是某个方法的返回值的类型,或者是参数类型(看下面代码理解)。

      在编程时,我们新建一个类的属性的时候,有时候可能会不确定该属性的类型,让其由用户来决定,这时候我们便可以使用泛型

      class Person<E>{
          //E表示数据类型
          E s;
          //E也可以是参数类型
          public Person(E s) {
              this.s = s;
          }
          //E做返回类型
          public E getS() {
              return s;
          }
      }
      

      我们在Generic02中,可以根据所需来声明E的类型

      package com.generic;
      
      public class Generic02 {
          public static void main(String[] args) {
              //当我们要s为String类型的时候
              Person<String> stringPerson = new Person<String>("jack");
              //当我们要s为int类型的时候
              Person<Integer> person = new Person<Integer>(1);
          }
      }
      

      E的数据类型在定义Person时指定,即在编译期间就可以确定E的类型

      image03

  • 泛型的语法

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

    其中,K,T,V不代表值,而是表示类型

    任意字母都可以。常用T表示

  • 泛型的应用实例

    1. 创建三个学生对象
    2. 把学生对象放到HashMap中,而且学生名字为key,学生对象为value
    3. 用两种方式遍历学生对象

    代码演示

    创建Student类

    class Student{
        private String name;
        private int age;
        public Student(String name,int age) {
            this.name = name;
            this.age = age;
        }
    
        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 "Student{" +
                    "name='" + name + '\'' +
                    ", age=" + age +
                    '}';
        }
    }
    

    创建测试类GenericExercise

    package com.generic;
    
    import java.util.*;
    
    public class GenericExercise {
        public static void main(String[] args) {
            Student jack = new Student("jack", 10);
            Student tom = new Student("tom", 13);
            Student mary = new Student("mary", 15);
            HashMap<String,Student> hashMap = new HashMap<String,Student>();
            hashMap.put(jack.getName(),jack);
            hashMap.put(tom.getName(),tom);
            hashMap.put(mary.getName(),mary);
    
            Set<String> strings = hashMap.keySet();
            Iterator<String> iterator = strings.iterator();
            while (iterator.hasNext()) {
                String next =  iterator.next();
                System.out.println(next+":"+hashMap.get(next));
            }
            System.out.println("==============");
            Set<Map.Entry<String, Student>> entries = hashMap.entrySet();
            for (Map.Entry<String, Student> entry : entries) {
                System.out.println(entry.getKey()+"="+entry.getValue());
            }
        }
    }
    

    运行结果

    image04

泛型的使用细节

  • interface List<T,V>等泛型的声明中,其中T,V只能是引用类型,不能为基本数据类型!

    //正确的定义
    List <Integer> list1 = new ArrayList<Integer>();
    //错误的定义	
    List <int> list2 = new ArrayList<int>();
    
  • 在给泛型指定的具体类型后,可以传入该类型或该子类类型(继承)。

    class Detail{
        public static void main(String[] args){
            Pig<A> piga = new Pig<A>(new A());
            Pig<A> piga = new Pig<A>(new B()); 
        }
    }
    class A{
    
    }
    class B extends A{
    
    }
    class Pig<E>{
        E e;
        publiu Pig(E e){
            this.e = e;
        }
    }
    
  • 泛型的使用方式

    //传统写法
    List <Integer> list1 = new ArrayList<Integer>();
    //实际开发中,通常使用简写。因为编译器会进行类型推断,所以后面的可以不写
    List <Integer> list1 = new ArrayList<>();//推荐这种写法!
    
  • 如果是这样写 List list1 = new ArrayList(); 也会使用泛型,默认是Object

    List list1 = new ArrayList();
    //等价于
    List <Object> list1 = new ArrayList<>();
    

泛型课堂练习题

定义Employee类

  1. 该类包含:private成员变量 name,sal,birthday,其中birthday为Mydate类的对象;

  2. 为每一个属性定义getter,setter方法;

  3. 重写toString方法输出name,sal,birthday

  4. MyDate类包含:private成员变量month,day,year;并为每一个属性定义getter,setter方法;

  5. 创建Employee类的三个对象,并把这些对象放入ArrayList集合中(ArrayList需要使用泛型来定义),对集合中的元素进行排序,并遍历输出;

  6. 排序方式:调用ArrayList的sort方法,传入Comparator对象,先按照name排序,如果name相同,则按照生日日期的先后排序。

    创建MyDate类

    package com.generic;
    
    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 "MyDate{" +
                    "year=" + year +
                    ", month=" + month +
                    ", day=" + day +
                    '}';
        }
    	//实现Comparable的方法,让Birthday的比较封装在MyDate类中
        //封装后代码的维护性和可复用行大大提高!
        @Override
        public int compareTo(MyDate o) {
            //按照year的大小进行排序
            int yearMinus = this.getYear() - o.getYear();
            //如果year不相同,返回比较值
            if (yearMinus != 0){
                return yearMinus;
            }
            //按照month的大小进行排序
            int monthMinus = this.getMonth() - o.getMonth();
            if (monthMinus != 0){
                return monthMinus;
            }
            //如果year与month相同,按照day的大小进行排序
            return this.getDay() - o.getDay();
        }
    }
    

    创建Employee类

    package com.generic;
    
    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 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;
        }
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    
        @Override
        public String toString() {
            return "\nEmployee{" +
                    "name='" + name + '\'' +
                    ", sal=" + sal +
                    ", birthday=" + birthday +
                    '}';
        }
    
    }
    

    创建测试类GenericExercise02

    package com.generic;
    
    import java.util.ArrayList;
    import java.util.Comparator;
    
    public class GenericExercise02 {
        public static void main(String[] args) {
            Employee jack = new Employee("jack", 1000, new MyDate(2000, 03, 20));
            Employee tom = new Employee("tom", 2000, new MyDate(1999, 02, 1));
            Employee mary = new Employee("jack", 3000, new MyDate(2000, 02, 19));
    
            ArrayList<Employee> employees = new ArrayList<>();
            employees.add(jack);
            employees.add(tom);
            employees.add(mary);
    
            System.out.println();
            employees.sort(new Comparator<Employee>() {
                @Override
                public int compare(Employee emp1, Employee emp2) {
                    if(!(emp1 instanceof Employee )&& !(emp2 instanceof Employee)){
                        System.out.println("类型不正确...");
                        return 0;
                    }
                    //按照name排序,字母的先后顺序
                    int i = emp1.getName().compareTo(emp2.getName());
                    //i不等于0说明name不相同,即通过名字即可完成排序,所以返回i;
                    if(i != 0){
                        return i;
                    }
                    //如果name长度相同,便按照birthday进行排序
                    return emp1.getBirthday().compareTo(emp2.getBirthday());
                }
            });
            System.out.println(employees);
        }
    }
    

    运行结果

    image05

自定义泛型

  • 自定义泛型类

    • 基本语法

      class 类名 <T,V...>{
      
      }
      
    • 注意细节

      1. 普通成员可以使用泛型(属性、方法)
      2. 使用泛型的数组,不能初始化(因为数组在创建的时候,不能确定泛型的类型,无法在内存开辟空间)
      3. 静态方法中不能使用类的泛型(因为static是与类相关的,在类加载时,对象还没有创建,而泛型是在对象创建时才产生的。)
      4. 泛型类的类型,是在创建对象时确定的(因为创建对象时,需要指定确定类型)
      5. 如果在创建对象时,没有指定类型,默认为Object
  • 自定义泛型接口

    • 基本语法

      interface 接口名 <T,E,R...>{
      
      }
      
    • 注意细节

      1. 在接口中,静态成员也不能使用泛型

      2. 泛型接口的类型,在继承接口或者实现接口时确定

        interface IA<T,V>{
            T get(V v);
        }
        //继承接口时确定泛型的类型
        //T->Double,V->String
        interface IB extends IA<Double,String>{
        
        }
        //当我们区实现IB接口时,因为IB在继承IA时指定了T为Double,V为String
        //所以在实现IA的方法时,会自动替换掉T,V
        class IBImp implements IB{
            @Override
            public Double get(String v){
        
            }
        }
        
      3. 没有指定类型,默认为Object

      4. 在jdk8中,default方法也能使用泛型

  • 自定义泛型方法

    • 基本语法

      修饰符 <T,R>返回类型 方法名(参数列表){
      
      }
      
    • 注意细节

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

    1. 泛型不具备继承性

      List <Object> list = new ArrayList<String>();//系统会报错
      

JUnit

  1. 一个类有很多功能代码需要测试,为了测试,就需要写入到main方法中
  2. 如果有多个功能代码测试,就需要来回注销,切换很麻烦
  3. 而使用JUnit便可以很好的解决上述问题
  • 基本介绍

    1. JUnit是一个Java语言的单元测试框架
    2. 多数Java的开发环境都已经继承了JUnit作为单元测试的工具
  • 使用

    在需要测试的文件上面增加一个@Test即可

Built with Hugo
Theme Stack designed by Jimmy