0%

泛型

介绍

Java 泛型(generics)是 JDK1.5 中引入的一个新特性, 泛型提供了编译时类型安全检测机制,该机制允许程序员在编译时检测到非法的类型,在此之前java都是使用object来进行完成这种功能,但是object的向下转型会带来一些类型转化的问题,所以才加入了泛型。

讲解

一、泛型的引入

在jdk1.5以前想要实现类似于C++模板的功能时,一般都是使用object实现,如:实现一个point类,里面有x、y两个数据成员,但是对于x、y类似是不确定的,有时候需要int型的x、y,有时候需要float型的x、y。其实现如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
class Point{
private Object x;
private Object y;

public void setX(Object x) {
this.x = x;
}


public void setY(Object y) {
this.y = y;
}


public Object getX() {
return x;
}


public Object getY() {
return y;
}
}

这种做法是基本满足使用要求,但是如果在某些情况下,很容易发生Object向下转型带来的问题,如以下代码:

1
2
3
4
5
6
7
8

public static void main(String[] args) {
Point point = new Point();
point.setX(1);
point.setY(2);
Float x = (Float) point.getX();
Float y = (Float) point.getY();
}

运行时就会发错误(如下图),即类型转化所带来的问题,并且这种错误,在编译时不会显示出来,只会在运行时显示,所以针对这种情况java引进了泛型。

所以如果使用泛型改写上述代码就应该是这样

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
class Point<T>{
private T x;
private T y;


public void setX(T x) {
this.x = x;
}


public void setY(T y) {
this.y = y;
}

public T getX() {
return x;
}


public T getY() {
return y;
}
}

使用起来就变成了这样Point<Integer>point = new Point<>(),如果这时候调用如果类型不对,会在编译的时候就爆出错误(如下图),方便开发人员及时的处理错误。

泛型2

另外如果申明了泛型,但是使用时不加<类型>,java还是会把里面的T当作Object处理,如上面的Point<Integer>point = new Point<>(),我这样用Point point = new Point(),并不会报错,而且会把x、y直接当作Object类型,也算时一种向下兼容,防止以前写的代码出问题。

二、泛型的使用

1.泛型类

泛型类使用起来比较简单,如上面的Point,就是泛型类的一种使用,不过需要注意的是泛型的参数字母可以任意定义,并且参数数量也没有什么限制,在这里就不过多举例。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
//此处T可以随便写为任意标识,常见的如T、E、K、V等形式的参数常用于表示泛型
//在实例化泛型类时,必须指定T的具体类型
public class Generic<T>{

private T key;

public Generic(T key) {
this.key = key;
}

public T getKey(){
return key;
}
}

如何实例化泛型类:

1
Generic<Integer> genericInteger = new Generic<Integer>(123456);

2.泛型接口

泛型接口定义和泛型类相似,可以参考如下代码

1
2
3
interface IMessage<T>{
public void print(T t);
}

但是值得注意的是,接口实现时,有两种方式:

  • 继续使用泛型,不指定类型:
1
2
3
4
5
6
7
class IMessageImp <T> implements IMessage<T>{

@Override
public void print(T t) {
// TODO Auto-generated method stub
}
}
  • 使用具体的类型
1
2
3
4
5
6
7
8
9
class IMessageImp implements IMessage<String>{

@Override
public void print(String t) {
// TODO Auto-generated method stub
System.out.println(t);
}

}

两种方式都有使用,需要都掌握

3.泛型方法

泛型方法,个人感觉使用应该不多,但是有必要了解一下,因为在jdk文档当中有一些这样的例子。

1
2
3
public <T> T[] fun(T...t){
return t;
}

4.通配符

通配符是为了配合泛型类等做参数而使用的,为了防止函数只能对一个具体的参数进行处理而产生的。

常用的 T,E,K,V,?

本质上这些个都是通配符,没啥区别,只不过是编码时的一种约定俗成的东西。比如上述代码中的 T ,我们可以换成 A-Z 之间的任何一个 字母都可以,并不会影响程序的正常运行,但是如果换成其他的字母代替 T ,在可读性上可能会弱一些。通常情况下,T,E,K,V,? 是这样约定的:

  • ? 表示不确定的 java 类型
  • T (type) 表示具体的一个java类型
  • K V (key value) 分别代表java键值中的Key Value
  • E (element) 代表Element
无界通配符

类型通配符一般是使用?代替具体的类型参数。例如 List 在逻辑上是List,List 等所有List<具体类型实参>的父类。

1
2
3
public static void getData(List<?> data) {
System.out.println("data :" + data.get(0));
}

除了上述的?通配符,还衍生出了如下两个通配符:

  • ?extends:设置泛型上限,参数和方法申明上都可以使用

    如:?extends Number,表示只能设置Number或者其子类,例如:Integer、Double,就是说泛型的上限就是Number

1
2
3
4
5
class Point<T extends Number >{

private T x;
private T y;
}
1
2
3
public void fun(List<? extends Number> list){
//无实际意义,只是简单举例
}
  • ?super:设置泛型下限,只能设置在参数上

    如:?super String,表示只能设置String或者其父类,例如:Object,就是说泛型的下限就是String

1
2
3
public void fun(List<?super Number> list){
//无实际意义,只是简单举例
}

泛型3

参考:https://juejin.im/post/6844903917835419661

总结

java泛型以前有所了解但是使用不多,这次系统的复习一下,做个小结。