1、什么是泛型
泛型的本质是参数化类型,即给类型指定一个参数,然后在使用时再指定此参数具体的值,这个类型就可以在使用时决定了。这种参数类型可以用在类、接口和方法中,分别被称为泛型类、泛型接口、泛型方法。
2、 为什么需要泛型
Java在引入泛型之前都是使用Object来实现类似泛型的功能。
public class A {
private Object b;
public void setB(Object b) {
this.b = b;
}
public Object getB() {
return b;
}
}
--------------------------------------
A a=new A();
a.setB(1);
int b=(int)a.getB();//需要做类型强转
String c=(String)a.getB();//运行时,ClassCastException
上面代码中需要手动进行类型强转,并且在编译时无法进行类型检查,只能在运行期才能检查出来,这样就很容易造成类型转换异常。
Java中引入泛型最主要的目的是将类型检查工作提前到编译时期,将类型强转(cast)工作交给编译器,从而让你在编译时期就获得类型转换异常以及去掉源码中的类型强转代码。
3、泛型类的定义
public class Point<T> {
private T x;
private T y;
public T getX() {
return x;
}
public void setX(T x) {
this.x = x;
}
public T getY() {
return y;
}
public void setY(T y) {
this.y = y;
}
}
泛型类的定义很简单:只需要在类名后加上<T>即可定义一个泛型类
,其中T并不是固定的,下面看下泛型类的使用。
Point<Integer> pointInt = new Point<Integer>();
pointInt.setX(1);
Point<Float> pointFloat = new Point<Float>();
pointFloat.setX(1.3f);
泛型类的使用也很简单,只需要在类名后的<>
中指明泛型类具体类型,注意T是派生自Object的,所以T不能指定为基本类型。
从泛型类的使用也可以看出泛型的好处:如果不使用泛型,则需要创建两个完全相同的Point类,只不过其中x、y的类型不同而已。
如果在Point类中有一个属性name的类型也想定义成泛型类型,该怎么办?其实很简单,代码如下:
public class Point<T, U> {
private T x;
private T y;
private U name;
public T getX() {
return x;
}
public void setX(T x) {
this.x = x;
}
public T getY() {
return y;
}
public void setY(T y) {
this.y = y;
}
public U getName() {
return name;
}
public void setName(U name) {
this.name = name;
}
}
可以看到只需要在类名后的<>
中将多个泛型类型用逗号隔开即可。
4、泛型接口定义
和定义泛型类完全一样的,只需要在接口名后加上<T>
即可。
public interface Info<T> {
public T getVar() ;
public void setVar(T var);
}
泛型接口的使用主要有以下两种方式:
4.1、方式一
public class InfoImpl implements Info<String> {
@Override
public String getVar() {
return null;
}
@Override
public void setVar(String var) {
}
}
可以看到在实现接口Info时,我们指定接口的泛型类型为String类型,如果此时我们并不想写死泛型的类型,而是交由调用者指定类型,该怎么实现呢?
4.2、方式二
public class InfoImpl<T> implements Info<T> {
@Override
public T getVar() {
return null;
}
@Override
public void setVar(T var) {
}
}
这里我们将泛型接口Info<T>
的实现类定义成了一个泛型类InfoImpl<T>
,这样在我们构造泛型类InfoImpl<T>
时就会将传入的T
传递给泛型接口Info<T>
中,这样也就实现了由用户决定具体的类型了。
5、泛型函数的定义
public class ClassTest {
public <T> T test(T t) {
return t;
}
}
泛型函数的定义需要在返回值前加上<T>
,下面看下泛型方法的使用
ClassTest classTest = new ClassTest();
classTest.<String>test("Hello"); //使用方式一
classTest.test("Hello"); //使用方式二
方式一和方式二的区别:
- 1、方式一和调用普通方法的区别就在于在调用方法前加上了
<String>
指明了T的类型。 - 2、方式二不同于方式一,省略了
<String>
内部会根据传入的参数来识别T的类型。
4、Class<T>的用法
public <T> void parse(Class<T> classObject,T object){
}
上面这个函数接收两个参数:
- 1、Class<T> classObject:表示接收的是泛型类的class对象
- 2、T object:表示接收的是泛型类的对象
6、泛型的限制
如果我们在使用泛型时,限制泛型类型为某个类的子类,那该怎么办呢?
Java中提供了一个关键字extends
来限定泛型的类型,下面看下具体的使用,比如泛型类的类型只允许是Number
的子类:
public class InfoImpl<T extends Number> {
public T getVar() {
return null;
}
public void setVar(T var) {
}
}
使用如下:
val infoImpl = InfoImpl<Float>()
infoImpl.setVar(12.0f)
如果没有通过extends
限制泛型的上限,泛型的上限默认是Object,此时限定泛型的上限为Number
,那么在构造InfoImpl指定泛型的类型就必须是Number
的子类,否则就会报错。
如果想给泛型设定多个上限,则可通过&
连接
public class InfoImpl<T extends Number & Serializable> {}
注意这里的extends并不是继承的意思,extends后可以是类也可以是接口,只是限定T为某个类或接口的子类。不过&
连接的必须是接口,Java是不允许多继承的。
网友评论