泛型概念
什么是泛型?
泛型的本质就是参数化类型。我们知道定义一个变量的时候会为其指定一个类型,比如定义Student类的对象Student xiaoming,Student即为变量xiaoming的类型,现在我们把这个具体的类型参数化,也就是说用参数变量来代替xiaoming的类型,这就是参数化类型。
当参数化类型被应用在类、接口、方法中的时候,对应的即为泛型类、泛型接口、泛型方法。
为什么要引入泛型?
不难理解,泛型编程可以使得编写的代码能被不同的类型对象所重用,只要传递不同的类型参数实参给类型参数就可以实现代码重用。
比如我们想定义一个集合分别存储String对象和Student对象,我们可不想设计一个StringArray来存String对象,再另外设计一个StudentArray来存Student对象。
我们知道ArrayList类就可以解决这个问题:
1 | public class Main |
我们可以向ArrayList对象存入String对象和Student对象,但是有两个问题:
- arrayList.get()获取元素的时候,需要进行强制类型转换,否则会有编译问题;
- 我们看到String world = (String)arrayList.get(1);并没有报编译错误,但是在运行的时候,会报类型转换异常。
1 | Exception in thread "main" java.lang.ClassCastException: com.Student cannot be cast to java.base/java.lang.String |
之所以有这两个问题,是因为不知道arrayList存入的元素类型,为了在编译阶段就解决这些问题,引入泛型,给ArrayList传入类型参数:
1 | ArrayList<String> arrayList = new ArrayList <>(); |
指定了类型参数,那么arrayList.add(xiaoming)就不会报编译错误;而且get方法也不需要进行强制类型转换了。
泛型类
将类的成员变量类型参数化,即为泛型类。
定义一个普通类Result
1 | package com; |
有一个String类型的成员变量,我们将其参数化,即得到泛型类:
1 | package com; |
复用泛型类:
1 | package com; |
这样就实现了对泛型类的复用,否则就得定义一个类处理String成员,再定义另外一个类来处理Integer成员,这里引入了类型变量T,用<>括起来放在类名后面,泛型类也可以有多个类型变量,比如public class Result<T,U>{…}。java中用E表示集合的元素类型,用K和V表示map中的key和value的类型,用T以及临近的U和S表示任意类型。
泛型接口
泛型接口和泛型类比较类似,将成员或方法类型参数化,看一个泛型接口的例子:
1 | package com; |
定义一个类实现该接口:
1 | package com; |
泛型方法
将方法的形参以及返回值参数类型化,即得到泛型方法:
1 | public static <T> T getMiddle(T[] num) |
这样只要是想获取数组中间元素,给getMiddle方法,传入该数组即可:
1 | package com; |
结果输出:
1 | kobe |
泛型方法可以定义在普通类中,也可以定义在泛型类中。