1. 泛型概述
1.1 生活中的例子
- 举例2:超市购物架上很多瓶子,每个瓶子装的是什么,有标签
Java中的泛型,就类似于上述场景中的标签
。
1.2 泛型的引入
在Java中,我们在声明方法时,当在完成方法功能时如果有未知的数据
需要参与,这些未知的数据需要在调用方法时才能确定,那么我们把这样的数据通过形参
表示。在方法体中,用这个形参名来代表那个未知的数据,而调用者在调用时,对应的传入实参
就可以了。
受以上启发,JDK1.5设计了泛型的概念。==泛型即为“类型参数
”,就是在定义类、接口和方法时使用类型参数(type parameter)。这些类型参数在实例化泛型类或调用泛型方法时被具体的类型所替代。泛型的主要作用是提高代码的安全性和可复用性。使用泛型,我们可以在编译时检查类型是否匹配,避免在运行时出现类型转换错误。==
举例:
java.lang.Comparable
接口和java.util.Comparator
接口,是用于比较对象大小的接口。这两个接口只是限定了当一个对象大于另一个对象时返回正整数,小于返回负整数,等于返回0,但是并不确定是什么类型的对象比较大小。JDK5.0之前只能用Object类型表示,使用时既麻烦又不安全,因此 JDK5.0 给它们增加了泛型。
其中<T>
就是类型参数,即泛型。
2. 使用泛型举例
自从JDK5.0引入泛型的概念之后,对之前核心类库中的API做了很大的修改,例如:JDK5.0改写了集合框架中的全部接口和类、java.lang.Comparable接口、java.util.Comparator接口、Class类等。为这些接口、类增加了泛型支持,从而可以在声明变量、创建对象时传入类型实参。
2.1 集合中使用泛型
2.1.1 举例
集合中没有使用泛型时:
集合中使用泛型时:
Java泛型可以保证如果程序在编译时没有发出警告,运行时就不会产生ClassCastException异常。即,把不安全的因素在编译期间就排除了,而不是运行期;既然通过了编译,那么类型一定是符合要求的,就避免了类型转换。
同时,代码更加简洁、健壮。
把一个集合中的内容限制为一个特定的数据类型,这就是generic背后的核心思想。
举例:
举例:
2.1.2 练习
练习1:
练习2:编写一个简单的同学通迅录
需求说明:
- 查询所有通讯录的同学信息。
- 输入姓名,根据姓名查询指定同学信息。如果该姓名不存在,输出提示信息。
- 添加同学,姓名重复的不能添加。
- 根据学员姓名删除学员。
- 按姓名排序查询学员。
分析:
- 使用HashMap<K,V>存储同学信息,使用同学姓名做key,同学对象做value。
- 同学对象包含的属性有:姓名、年龄、住址、爱好等。
2.2 比较器中使用泛型
2.2.1 举例
使用泛型之前:
使用泛型之后:
2.2.2 练习
(1)声明矩形类Rectangle,包含属性长和宽,属性私有化,提供有参构造、get/set方法、重写toString方法,提供求面积和周长的方法。
(2)矩形类Rectangle实现java.lang.Comparable接口,并指定泛型为,重写int compareTo(T t)方法,按照矩形面积比较大小,面积相等的,按照周长比较大小。
(3)在测试类中,创建Rectangle数组,并创建5个矩形对象
(4)调用Arrays的sort方法,给矩形数组排序,并显示排序前后的结果。
2.3 相关使用说明
在创建集合对象的时候,可以指明泛型的类型。
具体格式为:List list = new ArrayList();
JDK7.0时,有新特性,可以简写为:
List list = new ArrayList<>(); //类型推断
泛型,也称为泛型参数,即参数的类型,只能使用引用数据类型进行赋值。(不能使用基本数据类型,可以使用包装类替换)
集合声明时,声明泛型参数。在使用集合时,可以具体指明泛型的类型。一旦指明,类或接口内部,凡是使用泛型参数的位置,都指定为具体的参数类型。如果没有指明的话,看做是Object类型。
3. 自定义泛型结构
3.1 泛型的基础说明
1、<类型>这种语法形式就叫泛型。
2、在哪里可以声明类型变量<T>
- 声明类或接口时,在类名或接口名后面声明泛型类型,我们把这样的类或接口称为
泛型类
或泛型接口
。
- 声明方法时,在【修饰符】与返回值类型之间声明类型变量,我们把声明了类型变量的方法,称为泛型方法。
3.2 自定义泛型类或泛型接口
当我们在类或接口中定义某个成员时,该成员的相关类型是不确定的,而这个类型需要在使用这个类或接口时才可以确定,那么我们可以使用泛型类、泛型接口。
3.2.1 说明
① 我们在声明完自定义泛型类以后,可以在类的内部(比如:属性、方法、构造器中)使用类的泛型。
② 我们在创建自定义泛型类的对象时,可以指明泛型参数类型。一旦指明,内部凡是使用类的泛型参数的位置,都具体化为指定的类的泛型类型。
③ 如果在创建自定义泛型类的对象时,没有指明泛型参数类型,那么泛型将被擦除,泛型对应的类型均按照Object处理,但不等价于Object。
④ 泛型的指定中必须使用引用数据类型。不能使用基本数据类型,此时只能使用包装类替换。
⑤ 除创建泛型类对象外,子类继承泛型类时、实现类实现泛型接口时,也可以确定泛型结构中的泛型参数。
如果我们在给泛型类提供子类时,子类也不确定泛型的类型,则可以继续使用泛型参数。
我们还可以在现有的父类的泛型参数的基础上,新增泛型参数。
3.2.2 注意
① 泛型类可能有多个参数,此时应将多个参数一起放在尖括号内。比如:<E1,E2,E3>
② JDK7.0 开始,泛型的简化操作:ArrayList flist = new ArrayList<>();
③ 如果泛型结构是一个接口或抽象类,则不可创建泛型类的对象。
④ 不能使用new E[]。但是可以:E[] elements = (E[])new Object[capacity];
参考:ArrayList源码中声明:Object[] elementData,而非泛型参数类型数组。
⑤ 在类/接口上声明的泛型,在本类或本接口中即代表某种类型,但不可以在静态方法中使用类的泛型。
⑥ 异常类不能是带泛型的。
3.2.2 举例
举例1:
举例2:
举例3:
3.2.3 练习
练习1:
声明一个学生类,该学生包含姓名、成绩,而此时学生的成绩类型不确定,为什么呢,因为,语文老师希望成绩是“优秀”、“良好”、“及格”、“不及格”,数学老师希望成绩是89.5, 65.0,英语老师希望成绩是’A’,’B’,’C’,’D’,’E’。那么我们在设计这个学生类时,就可以使用泛型。
练习2:
代码实现:
3.3 自定义泛型方法
==如果我们定义类、接口时没有使用<泛型参数>,但是某个方法形参类型不确定时,这个方法可以单独定义<泛型参数>。==
3.3.1 说明
- 方法,也可以被泛型化,与其所在的类是否是泛型类没有关系。
- 泛型方法中的泛型参数在方法被调用时确定。
- 泛型方法可以根据需要,声明为static的。
3.3.2 举例
3.3.3 练习
练习1: 泛型方法
编写一个泛型方法,实现任意引用类型数组指定位置元素交换。
public static void method1( E[] e,int a,int b)
练习2: 泛型方法
编写一个泛型方法,接收一个任意引用类型的数组,并反转数组中的所有元素
public static void method2( E[] e)
4. 泛型在继承上的体现
有如下问题:
1. 类SuperA是类A的父类,则G<SuperA>
与G<A>
的关系。
G<SuperA>
与G<A>
没有关系,是并列的,如下:ArrayList<Object>
和ArrayList<String>
没有关系
类SuperA是类A的父类或接口,则SuperA与A的关系。
SuperA<G>与A<G>
是有继承关系的,如:List<String>和ArrayList<String>
综上:也就是说继承必须要泛型类型一致
思考:对比如下两段代码有何不同:
片段1:
片段2:
5. 通配符的使用
当我们声明一个变量/形参时,这个变量/形参的类型是一个泛型类或泛型接口,例如:Comparator类型,但是我们仍然无法确定这个泛型类或泛型接口的类型变量的具体类型,此时我们考虑使用类型通配符 ? 。
5.1 通配符的理解
使用类型通配符:?
比如:List<?>
,Map<?,?>
List<?>
是List<String>
、List<Object>
等各种泛型List的父类。
5.2 通配符的读与写
写操作:
将任意元素加入到其中不是类型安全的:
因为我们不知道c的元素类型,我们不能向其中添加对象。add方法有类型参数E作为集合的元素类型。我们传给add的任何参数都必须是一个未知类型的子类。因为我们不知道那是什么类型,所以我们无法传任何东西进去。
唯一可以插入的元素是null,因为它是所有引用类型的默认值。
读操作:
另一方面,读取List<?>的对象list中的元素时,永远是安全的,因为不管 list 的真实类型是什么,它包含的都是Object。
举例1:
举例2:
5.3 使用注意点
注意点1:编译错误:不能用在泛型方法声明上,返回值类型前面<>不能使用?
注意点2:编译错误:不能用在泛型类的声明上
注意点3:编译错误:不能用在创建对象上,右边属于创建集合对象
5.4 有限制的通配符
举例4:
5.5 泛型应用举例
举例1:泛型嵌套
举例2:个人信息设计
用户在设计类的时候往往会使用类的关联关系,例如,一个人中可以定义一个信息的属性,但是一个人可能有各种各样的信息(如联系方式、基本信息等),所以此信息属性的类型就可以通过泛型进行声明,然后只要设计相应的信息类即可。
6.随堂复习
1. 泛型的理解
2. 泛型在集合、比较器中的使用(重点)
- 集合:ArrayList、HashMap、Iterator
- 比较器:Comparable、Comparator
3. 自定义泛型类/泛型接口、泛型方法(熟悉)
- class Order{ }
- public 返回值类型 方法名(形参列表){}
- 具体的细节,见IDEA中的笔记。
4. 泛型在继承上的体现
5. 通配符的使用
- ? 的使用 (重点)
- 以集合为例:可以读取数据、不能写入数据(例外:null)
- ? extends A
- 以集合为例:可以读取数据、不能写入数据(例外:null)
- ? super A
- 以集合为例:可以读取数据、可以写入A类型或A类型子类的数据(例外:null)
7.企业真题
1. Java 的泛型是什么?有什么好处和优点?JDK 不同版本的泛型有什么区别?(软*动力)
泛型,是程序中出现的不确定的类型。
以集合来举例:把一个集合中的内容限制为一个特定的数据类型,这就是generic背后的核心思想。
jdk7.0新特性:
后续版本的新特性:
2. 说说你对泛型的了解(*软国际)
略