自定义泛型
1.1、泛型的定义介绍
在集合中,不管是接口还是类,它们在定义的时候类或接口名的后面都使用,当我们在使用的时候,可以指定其中的类型。如果当前的类或接口中并没有,我们在使用的时候也不能去指定类型。
举例:比如我们之前所学习的集合ArrayList类:
说明:在ArrayList类上有一个泛型的参数:E设计API的时候,设计者并不知道我们要用ArrayList存储哪种数据类型,所以他定义了一个泛型。然后当我们使用ArrayList的时候,我们知道要存储的类型,比如要存储String类型:
当我们new对象的时候,就把泛型E替换成了String,于是JVM就知道我们要存储的其实是String。
泛型:如果我们不确定数据的类型,可以用一个标识符来代替。
如果我们在使用的时候已经确定要使用的数据类型了,我们在创建对象的时候可以指定使用的数据类型。
泛型自定义格式:这里的标识符可以是任意的字母、数字、下划线和 $ 。但是这里一般规范使用单个大写字母。注意:自定义泛型也属于标识符,满足标识符的命名规则。1)数字不能开始;2)关键字不能作为标识符;
根据以上分析我们可以思考一个问题:既然我们学习过的集合类可以定义泛型,那么我们自己在描述类或接口的时候,是否也可以在自己的类或接口上定义泛型,然后别人使用的时候也可以指定这个类型呢?答案当然是可以的。
1.2、自定义泛型类(掌握)
泛型类:在定义类的时候,在类名的后面书写泛型。格式:
class 类名 { }
泛型参数其实就是标识符。
分析和步骤:1)创建测试类GenericDemo1 ,在这个类中定义一个main函数;2)定义一个泛型类Demo;3)在这个类中定义一个成员变量name,类型是泛型ABC类型;4)在定义一个非静态成员函数show,接收参数给name属性赋值,局部变量name的类型是ABC类型;5)在main函数中创建Demo类的对象,并指定泛型分别是String 和Integer类型;
package cn.xuexi.generic.demo1;/* * 自定义泛型类演示 * 在类上定义的泛型,当外界创建这个对象的时候,由创建者指定泛型的类型 * 在类上定义的泛型,类中的成员变量和函数都可以使用 */class Demo{ ABC name; public void show(ABC name) { this.name=name; }}public class GenericDemo1 { public static void main(String[] args) { //创建Demo类的对象 /* * 注意如果这里创建Demo类的对象时没有指定泛型类的类型时,这里ABC默认是Object类型 * 如下代码所示创建Demo类的对象的时候,指定的泛型类类型是String类型, * 那么Demo类中的泛型类ABC就是String类 * 同理,如果指定的数据类型是Integer,那么ABC就是Integer类 */ //Demo d=new Demo(); Demo d=new Demo(); //d.show("哈哈"); d.show(123); System.out.println(d.name); }}
说明:1)在类上定义的泛型,当外界在创建这个类的对象的时候,需要创建者自己来明确当前泛型的具体类型;2)在类上定义的泛型,在类中的方法上和成员变量是可以使用的;3)上述代码中如果创建Demo类的对象时没有指定泛型类的类型时,那么ABC默认是Object类型;4)上述代码中如果创建Demo类的对象时指定的泛型类类型是String类型,那么ABC默认是String类型;
自定义泛型类的练习:需求:使用自定义泛型模拟队列数据结构。
分析:由于队列是集合中的一种,可以存储任意类型的数据,而对于队列类我们不确定具体存储什么数据类型,所以可以在队列类QueueDemo中指定泛型,根据创建者来决定什么数据类型。
步骤:1)自定义一个类QueueDemo,在这个类中创建集合类LinkedList的对象list;2)在QueueDemo类中定义一个添加数据的函数addElement(E e),在这个函数中使用对象list调用LinkedList集合类中addLast(e)函数向队列中添加数据;3)定义一个函数getElement用来获得数据,返回值类型是E,并使用list对象调用LinkedList集合类中的removeFirst()函数;4)在定义一个函数isNull()判断队列中是否还含有数据,返回值类型是布尔类型;5)创建一个测试类GenericDemo,在这个类中创建队列类QueueDemo的对象qd,并在泛型位置指定数据类型String;6)使用对象qd调用添加数据的函数addElement(),向队列中添加几个数据;7)使用while循环遍历集合并取出;
自定义的队列类:
package cn.xuexi.generic.demo1;import java.util.LinkedList;/* * 模拟队列 * 由于队列是集合中的一种,可以存储任意类型的数据,而对于队列类我们不确定具体存储什么 * 数据类型,所以可以在队列类QueueDemo中指定泛型,根据创建者来决定什么类型 */public class QueueDemo { //创建LinkedList集合的对象 LinkedList list=new LinkedList(); //自定义函数向队列中添加数据 public void addElement(E e) { list.addLast(e); } //自定义函数取出队列中的数据 public E getElement() { return list.removeFirst(); } //自定义函数判断队列中是否还含有数据 public boolean isNull() { /* * list.isEmpty() 表示集合中没有数据返回true 有数据返回false * 我们这里这么书写:!list.isEmpty() 表示集合中有数据返回true 没有数据返回false */ return !list.isEmpty(); }}
定义测试类:
package cn.xuexi.generic.demo1;/* * 测试模拟队列的类 */public class GenericDemo2 { public static void main(String[] args) { //创建队列类的对象,并指定数据类型 QueueDemo qd = new QueueDemo(); //使用对象调用函数向队列中添加数据 qd.addElement("abc"); qd.addElement("hello"); qd.addElement("java"); //取出数据 while(qd.isNull()) { //表示有数据 String s = qd.getElement(); System.out.println(s); } }}
注意:对于自定义泛型类只有在创建这个类的对象的时候才可以指定泛型的类型。
1.3、在函数上使用泛型(掌握)
我们不仅可以在自己定义的类或接口上使用泛型,还可以在自定义的函数上使用泛型。虽然可以在类上定义泛型,但是有时类中的方法需要接收的数据类型和类上外界指定的类型不一致。也就是说对于某个函数而言参数的数据类型和所属类的泛型类型不一致了,这时我们可以在这个类中的这个方法上单独给这个方法设定泛型。
在函数上使用泛型的格式:
函数修饰符 函数返回值类型 方法名( 泛型名 变量名 ) { 函数体; }
说明:函数返回值类型前面的相当于定义了方法参数列表中泛型的类型。
代码演示如下图所示:
说明:1)类上的泛型是在创建这个类的对象的时候明确具体是什么类型;2)方法上的泛型是在真正调用这个方法时,传递的是什么类型,泛型就会自动变成什么类型;举例:上述代码中当调用者传递的是2,那么这个Q就代表Integer类型。如果调用者传递的是new Student(“班长”,19),那么这个Q类型就是Student类型。3)上述method函数中表示定义的泛型,而参数列表中的Q q是在使用泛型类型,而这里的Q类型具体由调用者来指定;
自定义泛型的练习:需求:自定义泛型方法,定义一个方法,可以打印任意一个数组的内容。打印String数组打印Integer数组
分析和步骤:1)分别定义一个String类型和Integer类型的数组,并分别存入数据;2)定义两个函数都是printArray,但是参数列表不同,一个接收String类型的数组,另一个接收是Integer类型的数组;3)分别遍历两个数组;4)虽然上述做法可以,但是只要遍历不同类型的数组就得定义一个重载的函数,麻烦,我们可以使用在函数上自定义泛型,函数的参数也是T[]类型的数组;
package cn.xuexi.generic.demo1;/* * 需求:自定义泛型方法,定义一个方法,可以打印任意一个数组的内容。 打印String数组 打印Integer数组 */public class GenericDemo4 { public static void main(String[] args) { //定义数组 String[] arr={"hello","world","java"}; Integer[]