泛型,即参数化类型,就是将操作的类型用一个参数来表示,可以用在类、接口和方法中,分别称为泛型类、泛型接口、泛型方法。
1、一个错误的例子
1 | class Test { |
这段代码中声明了一个List,可以存放任意类型,虽然编译运行都没有问题,但是十分不建议这么做。因为你无法保证List中保存的数据类型的一致性,如果把String.valueOf(alist.get(i))
替换成(String)alist.get(i)
,编译通过了运行时却会报错。
1 | List<String> alist = new ArrayList<>(); // 定义时指定参数类型 |
2、泛型特性
Java的泛型是通过类型擦除来实现的,编译期会检查参数类型是否合法,但是在生成的class文件中是不会有附加的类型信息的,例如List<Integer>
和List<String>
,在编译后都会变成List
,JVM看到的只是List
。这也就解释了,为什么第一个例子可以编译运行。
1 | class Test { |
1 | > true |
类型擦除后保留下来的就是原始类型,List<String>, List<Integer>
的原始类型就是List
。无论何时定义一个泛型类型,相应的原始类型都会被自动提供,类型变量被擦除并使用其限定类型(无限定的变量用Object)代替,如:
1 | class Pair<T> { |
Pair<T>
的原始类型为:
1 | class Pair<Object> { |
3、泛型类
1 | // T是泛型标识,可以是任意的字符,常见的有T、E、K、V |
定义了泛型类,在使用时并不一定要传入泛型类型实参,Pair<String> p1 = new Pair<>()
和Pair p2 = new Pair()
都可以用,然而如前面所述,后者不安全,不要这样用。
1 | class Test { |
4、泛型接口
1 | //定义一个泛型接口 |
1 | /** |
1 | /** |
5、泛型方法
泛型类,是在实例化类的时候指明泛型的具体类型;泛型方法,是在调用方法的时候指明泛型的具体类型 。
1 | /** |
1 | Object obj = genericMethod(Class.forName("com.test.test")); |
5.1 泛型方法的基本用法
1 | public class GenericTest { |
5.2 类中的泛型方法
1 | public class GenericFruit { |
5.3 泛型方法与可变参数
1 | public <T> void printMsg( T... args){ |
1 | printMsg("111",222,"aaaa","2323.4",55.55); |
5.4 静态方法与泛型
1 | public class StaticGenerator<T> { |
6 泛型通配符
在使用泛型的时候,我们还可以为传入的泛型类型实参进行上下边界的限制,如:类型实参只准传入某种类型的父类或某种类型的子类。
1 | public class Generic<T>{ |
如果有个方法的形参是泛型类,这个时候就需要用到通配符;
1 | public void showKeyValue(Generic<?> obj){ |
7 泛型上下边界
1 | public class Generic<T extends Number>{ |
1 | //在泛型方法中添加上下边界限制的时候,必须在权限声明与返回值之间的<T>上添加上下边界,即在泛型声明的时候添加 |
1 | public void showKeyValue(Generic<? extends Number> obj){ |
8 泛型数组
在java中是不能创建一个确切的泛型类型的数组的
1 | List<String>[] ls = new ArrayList<String>[10]; //wrong |