> 文档中心 > Java基础篇泛型的总结

Java基础篇泛型的总结

CSDN话题挑战赛第1期
活动详情地址:活动链接地址

参赛话题:Java学习记录
话题描述:可以记录一下平时学习Java中的一些知识点、心得、例题、常见的问题解决

泛型总结目录

  • 1. 泛型概述
    • 1.1 泛型的概念
    • 1.2 泛型的引入
  • 2. 泛型在集合中的使用
    • 2.1 泛型在集合Collection及Map中的使用
    • 2.2 泛型在 Comparable及Comparator中使用泛型
    • 2.3 自定义泛型结构
  • 3. 小知识点
    • 3.1 泛型在继承上的使用
    • 3.2 通配符的使用

1. 泛型概述

1.1 泛型的概念

了解

    所谓泛型,就是允许在定义类、接口时通过一个标识表示类中某个属性的类型或者是某个方法的返回值及参数类型。这个类型参数将在使用时(例如继承或实现这个接口,用这个类型声明变量、创建对象时)确定(即传入实际的类型参数,也称为类型实参)。总之,泛型可以理解为类型没有确定。

1.2 泛型的引入

泛型:标签(理解成是一个标签)
举例:
     中药店,每个抽屉外面贴着标签
     超市购物架上很多瓶子,每个瓶子装的是什么,有标签

重要性总结

    也就是说在定义的时候,元素的类型并不能确定,其他的地方是确定的(如:保存,管理),所以就把这个元素设置为泛型。例如:Collection,List,ArrayList 这个就是类型参数,即泛型。

2. 泛型在集合中的使用

2.1 泛型在集合Collection及Map中的使用

1. 问题引入:
    在集合中之所以要使用泛型,就是因为在使用了泛型之后就可以指明一个集合中存放数据的类型,在以后对集合中数据进行存取操作时,不会出现异常

使用泛型之前示例:
图示
Java基础篇泛型的总结
代码描述

//在集合中使用泛型    @Test    public void test1() { //使用泛型之前 //需求:存储学生成绩 ArrayList list = new ArrayList(); list.add(98); list.add(78); list.add(89); //线程不安全// list.add("Tom"); for (Object score : list) {     //会出现ClassCastException     Integer stuScore = (Integer) score;     System.out.println(stuScore); }    }

在Collection集合中使用泛型
图示
Java基础篇泛型的总结
代码描述

@Test    public void test2() { //使用泛型以后,可以指明添加到集合里面数据的类型 ArrayList<Integer> list = new ArrayList<>(); list.add(98); list.add(88); list.add(99); //当添加其他类型的数据时,编译过不了// list.add("Tom"); Iterator<Integer> iterator = list.iterator(); while (iterator.hasNext()) {     Integer stuScore = iterator.next();     System.out.println(stuScore); }    }

2. 泛型在Map集合中的使用
需要特别注意他的存储原理

Set<Map.Entry> entrySet = map.entrySet();
对于一个键值对,在底层被包装成一个实体entry,这个实体是存在于Map集合中的,所以泛型的类型会被声明为Map.Entry

@Test    public void test3() { Map<String, Integer> map = new HashMap<String, Integer>(); map.put("Jerry", 99); map.put("Jack", 98); map.put("Mary", 95); map.put("Jane", 99); //调用entry返回一个Entry实体,不过Entry时Map里面的一个接口,这一条记录又存放在Set集合里面 //所以类型为Set<Map.Entry> //实现遍历,在理解之后对于对象类型的生成直接alt+enter,在此之前要先理解原因 Set<Map.Entry<String, Integer>> entrySet = map.entrySet(); Iterator<Map.Entry<String, Integer>> iterator = entrySet.iterator(); while (iterator.hasNext()) {     Map.Entry<String, Integer> entry = iterator.next();     String key = entry.getKey();     Integer value = entry.getValue();     System.out.println(key + "---->" + value); }    }

3. 总结
①集合接口或集合类在jdk5.0时都修改为带泛型的结构。
②在实例化集合时,可以指明具体的泛型类型。
③指明完以后,在集合类或者接口中凡是定义类或接口时,内部结构(比如属性、方法、构造器)使用到类的泛型的位置,都指定为实例化的泛型类型
    比如:add(E e) ----->在指名具体的泛型类型之后变为add(Integer e)
④注意点:泛型的类型必须是类,不能是基本数据类型。需要用到基本数据类型的位置,用包装类替换
⑤如果实例化时,没有指明泛型的类型,默认类型为java.long.Object类型

2.2 泛型在 Comparable及Comparator中使用泛型

①Comparable的使用
了解内容
在Comparable接口实现中使用泛型,在对实现类对象进行比较的时候,就不需要在进行转化,直接比较对象的属性(按照既定的排序方法)即可
简化内容:
在使用泛型之前,没有规定集合中元素的类型,所以传过来的对象需要进行转化等一些列操作,在使用泛型之后就可以免去

示例
使用泛型之前

@Override    public int compareTo(Object o) { if (o instanceof Employee){     Employee employee = (Employee) o;     return this.name.compareTo(employee.name); }else {     throw new RuntimeException("输入的格式有误"); }    }

使用泛型之后

 @Override    public int compareTo(Employee o) { return this.name.compareTo(o.name);    }

②Comparator中使用

遇到的问题与在Comparable中同样问题,在传入参数的时候,指明了类型,所以不再需要进行类型的转化

样例:

使用泛型前

TreeSet set = new TreeSet(new Comparator() {     @Override     public int compare(Object o1, Object o2) {  if (o1 instanceof Employee && o2 instanceof Employee) {      Employee employee = (Employee) o1;      Employee employee1 = (Employee) o2;      MyDate birthday1 = employee.getBirthday();      MyDate birthday2 =employee1.getBirthday();      int minusYear = birthday1.getYear() - birthday2.getYear();      if (minusYear !=0){   return minusYear;      }else {   int minusMonth = birthday1.getMonth() - birthday2.getMonth();   if (minusMonth != 0){return minusMonth;   }else {return birthday1.getDay() - birthday2.getDay();   }      }  }  throw new RuntimeException("输入的数据类型不一致");     } });

使用泛型后

TreeSet<Employee> set = new TreeSet<>(new Comparator<Employee>() {  @Override  public int compare(Employee o1, Employee o2) {      MyDate b1 = o1.getBirthday();      MyDate b2 = o2.getBirthday();      return b1.compareTo(b2);  }     });

当然这里需要重写Compare方法

@Override    public int compareTo(MyDate m) { //比较年 int minusYear = this.getYear() - m.getYear(); if(minusYear != 0){     return minusYear; } //比较月 int minusMonth = this.getMonth() - m.getMonth(); if(minusMonth != 0){     return minusMonth; } //比较日 return this.getDay() - m.getDay();    }

2.3 自定义泛型结构

体会为主
①自定义泛型类

定义格式

//类名以Order为例public class Order <T>{//属性名T orderT;}

②自定义泛型方法
使用样例:
实现泛型数组数据添加到List集合

public <E> List<E> copyFromArrayToList(E[] arr){ ArrayList<E> list = new ArrayList<>(); for (E e: arr){     list.add(e); } return list;    }

泛型方法提醒
    泛型方法:在方法中出现了泛型的结构,泛型参数与类的泛型参数没有任何关系。换句话说:泛型方法所属的类是不是泛型类都没有关系
    在返回值类型前面加一个表示E是一个泛型
    泛型方法,可以声明为静态的。原因为:泛型参数是在调用方法时确定的,并非在实例化类时确定

泛型使用说明

①泛型类可能有多个参数,此时应将多个参数一起放在尖括号内。比如:
②泛型类的构造器如下:public GenericClass(){}。
③实例化后,操作原来泛型位置的结构必须与指定的泛型类型一致。
④泛型不同的引用不能相互赋值。
    尽管在编译时ArrayList和ArrayList是两种类型,但是,在运行时只有一个ArrayList被加载到JVM中。
⑤ 如果泛型结构是一个接口或抽象类,则不可创建泛型类的对象。
⑥泛型的简化操作:ArrayList flist = new ArrayList();
⑦ 泛型的指定中不能使用基本数据类型,可以使用包装类替换。例如:int–>Integer

说明此处有代码示例请参考下一篇文章

3. 小知识点

3.1 泛型在继承上的使用

重在理解

     虽然类A是类B的父类,但是G 和 G二者不具备子父类的关系,二者是并列的关系
补充:类A 是类B的父类,A 是 B 的父类

List和List的使用样例

 @Test    public void test1(){ //此时类型具有子父类的关系,相当于多态,可以直接赋值 Object obj = null; String str = null; obj = str;  Object[] arr1 = null; String[] arr2 = null; arr1 = arr2;  List<Object> list1 = new ArrayList<>(); List<String> list2 = new ArrayList<>();//      此时的list1和list2没有子父类的关系,两个都是List类型,不能直接赋值// list1 = list2;    }

3.2 通配符的使用

1.理解通配符

使用类型通配符:?
比如:List ,Map
List是List、List等各种泛型List的父类。

②读取List的对象list中的元素时,永远是安全的,因为不管list的真实类型是什么,它包含的都是Object。
③写入list中的元素时,不行。因为我们不知道c的元素类型,我们不能向其中添加对象。

2.通配符使用情景

    在没有使用泛型之前直接传入一个集合,进行操作即可,但是现在使用了泛型之后,需要区别集合的类型,要想消除代码的冗余,这时候就需要考虑使用通配的使用

使用前后对比

@Test    public void test2(){ List<String> list = null; ArrayList<String> list1= null; list = list1;    }    public void print(List<Object> list){    }    public void print1(List<String> list){    }

使用之后

public void print(List<?> list){ Iterator<?> iterator = list.iterator(); while (iterator.hasNext()){     System.out.println(iterator.next()); }    }

3.有限制条件的通配符的使用

? extends A:
     G 可以作为G
和G的父类,其中B是A的子类
? super A:
     G 可以作为G
和G的父类,其中B是A的父类类

使用样例

@Test    public void test4(){ List<? extends Person> list1 = null; List<? super Person> list2 = null; List<Person> list3 = null; List<Student> list4 = null; List<Object> list5= null; // list1 = list3; list1 = list4; //在 ? entends 父类 时,? 可以填的内容是 <=父类的,也就说?可以是子类,父类,但是不能时Object(父类的父类)// list1 = list5; //在 ? super 父类 时,? 可以填的内容是 >=父类的,也就说?可以是父类,父类的父类,但是不能时子类 list2 = list3;// list2 = list4; list2 = list5; //对含有通配符的集合的数据读取 Person person = list1.get(0); Object object = list2.get(0); //添加数据// list1.add(new Student());[] //对于? super A类型可以往集合里面添加数据 //子类可以赋值给父类,但是父类不能赋值给子类,所以在super 里面有Object(所有类的父类) list2.add(new Student()); list2.add(new Person());//[.....Object]    }