java学习笔记-泛型(韩顺平)

1. 泛型的理解和好处

  • 看一个需求

    1. 请编写程序,在ArrayList中,添加3个Dog对象

    2. Dog对象含有name和age,并输出name和age(要求使用getXxx())

      传统方式

      public class Generics_ {
          @SuppressWarnings({"all"})
          public static void main(String[] args) {
              ArrayList arrayList = new ArrayList();
              arrayList.add(new Dog("DaHuang",3));
              arrayList.add(new Dog("XiaoHua",4));
              arrayList.add(new Dog("TuanTuan",2));
              
              for (int i = 0; i < arrayList.size(); i++) {
                  Dog d = (Dog)arrayList.get(i);
                  //假如程序员不小心添加了一只猫,会抛出ClassCastException异常
                  System.out.println(d.getName() + "-" + d.getAge());
              }
          }
      }
      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;
          }
      ​
          @Override
          public String toString() {
              return "Dog{" +
                      "name='" + name + '\'' +
                      ", age=" + age +
                      '}';
          }
      }

      传统方法问题分析:

      1. 不能对加入到集合ArrayList中的数据类型进行约束(不安全)

      2. 遍历的时候需要进行类型转换,如果集合中数据量较大,对效率有影响

      泛型快速体验-用泛型解决前面的问题

      package com.mdklea.generics;
      ​
      ​
      import java.util.ArrayList;
      ​
      public class Generics02 {
          public static void main(String[] args) {
              //解读
              //1. 当我们 ArrayList<GDog> 表示存放到 ArrayList 集合中的元素是Dog类型
              //2. 如果编译器发现添加的类型不满足要求就会报错
              //3. 在遍历的时候可以直接取出GDog而不是Object
              ArrayList<GDog> dogs = new ArrayList<GDog>();
              dogs.add(new GDog("DaHuang",2));
              dogs.add(new GDog("XiaoHua",7));
              dogs.add(new GDog("XiaoMei",5));
              for (GDog dog :dogs) {
                  System.out.println(dog.getName() + "-" + dog.getAge());
              }
      ​
          }
      }
      class GDog {
          private String name;
          private int age;
      ​
          public GDog(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;
          }
      ​
          @Override
          public String toString() {
              return "GDog{" +
                      "name='" + name + '\'' +
                      ", age='" + age + '\'' +
                      '}';
          }
      }

  • 泛型的好处

    1. 编译时,检查添加元素的类型,提高了安全性

    2. 减少了类型转换的次数,提高效率

    3. 不再显示编译警告

2.泛型介绍

理解:泛(广泛)型(类型) => Integer,String,Dog

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

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

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

  4. 泛型的作用是:可以在类声明时通过一个标识符表示类中某个属性的类型,或者是某个方法返回值的类型,或者是参数类型。

  5. ss可以理解为一种能够接收数据类型的数据类型

    package com.mdklea.generics;
    ​
    public class Generics03 {
        public static void main(String[] args) {
            Person<String> stringPerson = new Person<String>("m1");
            System.out.println(stringPerson.f());
        }
    }
    ​
    class Person<E>{
        E s; //E表示 s的数据类型,该数据类型在定义Person对象的时候指定,即在编译期间,就确定E是什么类型
    ​
        public Person(E s) {  //E也可以是参数类型
            this.s = s;
        }
    ​
        public E f() {  //E也可以是返回类型
            return s;
        }
    }

  6. 特别强调:E具体的数值类型在定义Person对象的时候指定,即在编译期间,就确定E是什么类型

3.泛型的语法

  • 泛型的声明

    interface 接口<T>{} 和class 类<K,V>{}

    //比如List,ArrayList

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

    2. 任意字母都可以。常用T表示,是Type的缩写

  • 泛型的实例化

    要在类名后面指定类型参数的值(类型)。如:

    1. List<String> strList = new ArrayList<String>();

    2. Iterator<Customer> iterator = customers.iterator();

​
package com.mdklea.generics;
​
import java.util.*;
​
public class GenericExercise {
    @SuppressWarnings({"all"})
    public static void main(String[] args) {
        HashMap<String, Student> studentHashMap = new HashMap<>();
        HashSet<Student> studentHashSet = new HashSet<>();
        Student m1 = new Student("马1", 700);
        Student m2 = new Student("马2", 720);
        Student xxp = new Student("xxp", 730);
        studentHashMap.put(m1.getName(),m1);
        studentHashMap.put(m2.getName(),m2);
        studentHashMap.put(xxp.getName(),xxp);
        Set<Map.Entry<String, Student>> entries = studentHashMap.entrySet();
        for (Map.Entry entry :entries) {
            System.out.println(entry.getKey() + "-" + entry.getValue());
        }
        Set<String> set = studentHashMap.keySet();
        System.out.println("==============================");
        for (String stu :set) {
            System.out.println(stu + studentHashMap.get(stu));
        }
        System.out.println("======================================");
        studentHashSet.add(new Student("XiaoWang",583));
        studentHashSet.add(new Student("XiaoMei",432));
        studentHashSet.add(new Student("XiaoYe",283));
        for (Student stu :studentHashSet) {
            System.out.println(stu);
        }
​
        System.out.println("itit");
        Iterator<Student> iterator = studentHashSet.iterator();
        while (iterator.hasNext()) {
            Student next = iterator.next();
            System.out.println(next);
        }
    }
}
class Student {
    private String name;
    private int grade;
​
    public Student(String name, int grade) {
        this.name = name;
        this.grade = grade;
    }
​
    public String getName() {
        return name;
    }
​
    public void setName(String name) {
        this.name = name;
    }
​
    public int getGrade() {
        return grade;
    }
​
    public void setGrade(int grade) {
        this.grade = grade;
    }
​
    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", grade=" + grade +
                '}';
    }
}

​

4.泛型使用的注意事项和细节

  1. interface List <T>{},Public class HashSet<E> {}...等等

    说明:T,E只能是引用类型

    看看下面语句是否正确?

    List <Integer> list = new ArrayList<Integer> {}; // ok

    List<int>list2 = new ArrayList<int>{}; // 错误

  2. 在指定泛型具体类型后,可以传入该类型或者其子类类型

  3. 泛型使用形式

    List<Integer> list1 = new ArrayList<Integer> {};

    List<Integer>list2 = new ArrayList<> {};

    如果我们这样写 List list3 = new ArrayList(); 默认给它的泛型就是[<E> E就是Object]

5.课堂练习

package com.mdklea.generics;
​
import java.util.ArrayList;
import java.util.Comparator;
​
public class GenericExercise01 {
    @SuppressWarnings({"all"})
    public static void main(String[] args) {
        ArrayList<Employee> employees = new ArrayList<>();
        employees.add(new Employee("mda",30000,new Employee().OMyDate(6,10,2000)));
        employees.add(new Employee("mda",30000,new Employee().OMyDate(21,10,2000)));
        employees.add(new Employee("mda",30000,new Employee().OMyDate(2,10,2000)));
        employees.sort(new Comparator<Employee>() {
            @Override
            public int compare(Employee o1, Employee o2) {
                //比较名字
                int i = o1.getName().compareTo(o2.getName());
                //可以不写那么多else
                if (i != 0){
                    return i;
                }else {
                    i = (int)(o1.getSal() - o2.getSal());
                    if (i != 0){
                        return i;
                    }else {
                        //把年月日的写法写到MyDate中
                            return o1.getBirthday().compareTo(o2.getBirthday());
                        }
                    }
            }
        });
        for (Employee e :employees) {
            System.out.println(e);
        }
​
    }
}
​
@SuppressWarnings({"all"})
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 Employee() {
    }
    //不写内部类貌似更方便
    class MyDate implements Comparable<MyDate>{
        private int day;
        private int month;
        private int year;
​
        public MyDate(int day, int month, int year) {
            this.day = day;
            this.month = month;
            this.year = year;
        }
​
        public int getDay() {
            return day;
        }
​
        public void setDay(int day) {
            this.day = day;
        }
​
        public int getMonth() {
            return month;
        }
​
        public void setMonth(int month) {
            this.month = month;
        }
​
        public int getYear() {
            return year;
        }
​
        public void setYear(int year) {
            this.year = year;
        }
​
​
​
        @Override
        public String toString() {
            return day +
                    "-" + month +
                    "-" + year ;
        }
​
​
        @Override
        public int compareTo(MyDate o) {
            int yearMinus = this.year - o.year;
            if (yearMinus != 0){
                return yearMinus;
            }
            int monthMinus = this.month - o.month;
            if (monthMinus != 0){
                return monthMinus;
            }
            int dayMinus = this.day - o.day;
            return dayMinus;
        }
    }
​
    public String getName() {
        return name;
    }
​
    public void setName(String name) {
        this.name = name;
    }
​
    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;
    }
​
    @Override
    public String toString() {
        return "Employee{" +
                "name='" + name + '\'' +
                ", sal=" + sal +
                ", birthday=" + birthday +
                '}';
    }
    public MyDate OMyDate(int day,int month,int year){
        return new MyDate(day,month,year);
    }
}

6.自定义泛型

  • 基本语法

    class 类名<T,R...>{

    //...表示可以有多个成员

    }

  • 注意细节

    1. 普通成员可以使用泛型(属性或方法)

    2. 使用泛型的数组不能初始化

    3. 静态方法中不能使用类的泛型

    4. 泛型类的类型,是在创建对象时确定的(因为创建对象时,需要指定确定泛型)

    5. 如果在创建对象时,没有指定泛型,默认为Object

  • package com.mdklea.customgeneric;
    ​
    public class CustomGeneric_ {
        public static void main(String[] args) {
    ​
        }
    }
    //解读
    //1. Tiger后面有泛型,所以我们把 Tiger称为自定义泛型类
    //2.T R M 是泛型的标识符,一般是单个的大写字母
    //3.泛型的标识符可以有多个
    //4.普通成员可以使用泛型(属性,方法)
    //5.使用泛型的数组,不能初始化
    //6.静态方法不能使用类的泛型
    class Tiger<T,R,M> {
        String name;
        R r;//属性使用泛型
        M m;
        T t;
    ​
        //因为数组在创建时不能确定T的类型,就无法在内存开空间
        //T[] ts = new T[4];
        T[] ts;
        //因为静态是和类相关的,在类加载时,对象还没有创建
        //所以如果静态方法和静态属性使用了泛型,JVM就无法初始化
        //public static void m1(M m){
        //}
        public Tiger(String name, R r, M m, T t) {//构造器使用泛型
            this.name = name;
            this.r = r;
            this.m = m;
            this.t = t;
        }
    ​
        public String getName() {
            return name;
        }
    ​
        public void setName(String name) {
            this.name = name;
        }
    ​
        public R getR() { //返回类型使用泛型
            return r;
        }
    ​
        public void setR(R r) {//方法使用泛型
            this.r = r;
        }
    ​
        public M getM() {
            return m;
        }
    ​
        public void setM(M m) {
            this.m = m;
        }
    ​
        public T getT() {
            return t;
        }
    ​
        public void setT(T t) {
            this.t = t;
        }
    }
  • 自定义泛型接口

    • 基本语法

      interface 接口名<T,R,...>{ }

    • 注意细节

      1. 接口中,静态成员也不能使用泛型(这个和泛型类的规定一样)

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

      3. 没有指定类型,默认为Object

    package com.mdklea.customgeneric;
    ​
    public class CustomInterfaceGeneric {
        public static void main(String[] args) {
    ​
        }
    }
    /*
    泛型接口使用的说明
    1. 接口中,静态成员不能使用泛型
    2. 泛型接口的类型,是在继承接口或者实现接口时确定的
    3. 没有指定类型,默认为Object
     */
    interface IUsb<U,R> {
        //接口中的成员都是静态性质的
        int i = 10;
    ​
        //U name; 不能这样使用
    ​
        //普通方法中,可以使用接口泛型
        R get(U u);
        void hi(R r);
        void run(R r1,R r2,U u1,U u2);
    ​
        //JDK8中,可以在接口中,使用默认方法,也是可以使用泛型的
        default R methdod(U u) {
            return null;
        }
    }
    //在继承接口时,确定泛型接口类型
    interface IA extends IUsb<String ,Double> {}
    ​
    //当我们实现IA接口时,因为IA在继承IUsb 接口时,指定了 U为String R为Double
    //在实现IUsb接口的方法时,使用String替换U,Double替换R
    class AA implements IA {
    ​
        @Override
        public Double get(String s) {
            return null;
        }
    ​
        @Override
        public void hi(Double aDouble) {
    ​
        }
    ​
        @Override
        public void run(Double r1, Double r2, String u1, String u2) {
    ​
        }
    }
    ​
    //实现接口时直接就指定泛型接口的类型
    //给U 指定Integer 给R 制定了 Float
    //所以,当我们实现IUsb方法时,会使用Integer替换U,使用FLoat替换R
    class  BB implements IUsb<Integer,Float> {
        @Override
        public Float get(Integer integer) {
            return null;
        }
    ​
        @Override
        public void hi(Float aFloat) {
    ​
        }
    ​
        @Override
        public void run(Float r1, Float r2, Integer u1, Integer u2) {
    ​
        }
    }
    //没有指定类型,默认为Object
    //建议直接写成class CC implements IUsb<Object,Object>{}
    class CC implements IUsb { // 等价 class CC implements IUsb<Object,Object>{}
    ​
        @Override
        public Object get(Object o) {
            return null;
        }
    ​
        @Override
        public void hi(Object o) {
    ​
        }
    ​
        @Override
        public void run(Object r1, Object r2, Object u1, Object u2) {
    ​
        }
    }

  • 自定义泛型方法

    • 基本语法

      修饰符 <T,R...> 返回类型 方法名(参数列表) {}

    • 注意细节

      1. 泛型方法可以定义在普通类中,也可以定义在泛型类中

      2. 当泛型方法被调用时,类型会确定

      3. public void eat(E e){},修饰符后没有<T,R...> eat方法不是泛型方法,而是使用了泛型。

    • package com.mdklea.customgenericmethod;
      ​
      ​
      import java.util.ArrayList;
      ​
      @SuppressWarnings({"all"})
      public class CustomGenericMethod {
          public static void main(String[] args) {
              Car car = new Car();
              //当泛型方法被调用时,类型会确定
              car.fly("宝马",100);
              Fish<String, ArrayList> fish = new Fish<>();
              fish.hello(new ArrayList(),12.4f);
          }
      }
      ​
      /*
      泛型方法的使用:
          1. 泛型方法可以定义在普通类中,也可以定义在泛型类中
          2. 当泛型方法被调用时,类型会确定
          3. public void eat(E e) {} 修饰符后没有<T,R>eat方法不是泛型方法,
             而是使用了泛型。
       */
      class Car {
          public void run() {//普通方法
      ​
          }
          //泛型方法可以定义在普通类中,也可以定义在泛型类中
          public<T,R> void  fly(T t,R r) {//泛型方法
              System.out.println(t.getClass());
              System.out.println(r.getClass());
          }
      }
      class Fish<T,R> { //泛型类
          public void run() {
      ​
          }
          public<U,M> void eat(U u, M m) { //泛型方法
      ​
          }
      ​
          //说明
          //1. 下面这个方法不是泛型方法
          //2. 是hi方法使用了类声明的 泛型
          public void hi(T t) {
          }
          //泛型方法可以使用类声明的泛型,也可以使用自己声明的泛型
          public<K> void hello(R r,K k) {
              System.out.println(r.getClass());
              System.out.println(k.getClass());
          }
      }

7.泛型的继承和通配符

  • 泛型的继承和通配符说明:

    1. 泛型不具备继承性

      List<Object> list = new ArrayList<String> (); //对吗?

    2. <?>:支持任意泛型类型

    3. <? extends A>:支持A类以及A类的子类,规定了泛型的上限

    4. <? super A>:支持A类以及A类的父类,不限于直接父类,规定了泛型的下限

8.JUnit单元测试类

  • 为什么需要JUnit

    1. 一个类有很多功能代码需要测试,为了测试,就需要写到main方法中

    2. 如果有多个功能代码测试,就需要来回注销,切换很麻烦

    3. 如果可以直接运行一个方法,就方便很多,并且可以给出相关信息就好了 -> Junit

  • 基本介绍

    1. JUnit是一个Java语言的单元测试框架

    2. 多少java的开发环境都已经集成了JUnit作为单元测试的工具

8.章节练习

package com.mdklea.homework;
​
import org.junit.jupiter.api.Test;
​
import java.util.*;
​
@SuppressWarnings({"all"})
public class HomeWork01 {
    public static void main(String[] args) {
​
    }
    @Test
    public void testList() {
        DAO<User> dao = new DAO<>();
        dao.save("001",new User(1,10,"jack"));
        dao.save("002",new User(2,20,"mary"));
        dao.save("003",new User(3,30,"smith"));
        System.out.println(dao.list());
        dao.update("003",new User(3,20,"mdk"));
        System.out.println(dao.list());
        dao.delete("001");
        System.out.println(dao.list());
    }
}
@SuppressWarnings({"all"})
class DAO<T> {
    private Map<String, T> map = new HashMap<>();
​
    public void save(String id,T entity) {
        map.put(id,entity);
    }
​
    public T get(String id) {
        return map.get(id);
    }
​
    public void update(String id,T entity) {
        if (map.containsKey(id)){
            map.put(id,entity);
        }else {
            System.out.println("map中无此对象!");
        }
    }
​
    public List<T> list() {
        Collection<T> values = map.values();
        return new ArrayList<>(values);
    }
​
    public void delete(String id) {
        map.remove(id);
    }
​
}
class User {
    private int id;
    private int age;
    private String name;
​
    public User(int id, int age, String name) {
        this.id = id;
        this.age = age;
        this.name = name;
    }
​
    public int getId() {
        return id;
    }
​
    public void setId(int id) {
        this.id = id;
    }
​
    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 "User{" +
                "id=" + id +
                ", age=" + age +
                ", name='" + name + '\'' +
                '}';
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值