想必大家都有过这么个经历,当我们是采用面向对象的方法开发时,我们的增删该查,面对不同的实体,其实操作都差不多,或者可以是一样,在例如我要表示一个点,我可以写Point(String sx,String sy),也可以写成Point(Double dx,Double y),其实就是里边的参数类型不一样,其他的可能都一样,这个时候jdk就给了我们一个很好的解决方案,泛型。
泛型就是解决我们累心转换的问题,尤其是在编译期间不出问题,但是却会在程序运行时报错的问题,例如下边一段代码:
public void test(){
List list = new ArrayList();
list.add(32);
list.add("menghaibin");
list.add(32.222);
}
这个泛型方法在编译的时候是不会报错的,因为list.add()方法里边的参数类型是Object,而所有的类型的基类是object,所以程序编译不报错,但是如果程序运行呢,例如上边的代码,就一定会包错,并且是类型转化失败 java.lang.ClassCastException的错误了。原因很简单,我第一次加的是事一个int类型,那么以后就业应该加,而且即使我以前写的对了,我在取值的时候也需要强转一下,前提是类型我还能记得住。二这个时候我们来看看泛型为我们带来了什么。
泛型类
假如我要表示一个点,可能是坐标,也可能是位置,例如下边的代码:
public class Demo {
public static void main(String[] args){
// 实例化泛型类
Point<Integer, Integer> p1 = new Point<Integer, Integer>();
p1.setX(10);
p1.setY(20);
int x = p1.getX();
int y = p1.getY();
System.out.println("This point is:" + x + ", " + y);
Point<Double, String> p2 = new Point<Double, String>();
p2.setX(25.4);
p2.setY("东京180度");
double m = p2.getX();
String n = p2.getY();
System.out.println("This point is:" + m + ", " + n);
}
}
// 定义泛型类
class Point<T1, T2>{
T1 x;
T2 y;
public T1 getX() {
return x;
}
public void setX(T1 x) {
this.x = x;
}
public T2 getY() {
return y;
}
public void setY(T2 y) {
this.y = y;
}
}
与普通类的定义相比,上面的代码在类名后面多出了 <T1, T2>,T1, T2 是自定义的标识符,也是参数,用来传递数据的类型,而不是数据的值,我们称之为
类型参数。在泛型中,不但数据的值可以通过参数传递,数据的类型也可以通过参数传递。T1, T2 只是数据类型的占位符,运行时会被替换为真正的数据类型。
类型参数需要在类名后面给出。一旦给出了类型参数,就可以在类中使用了。类型参数必须是一个合法的标识符,习惯上使用单个大写字母,通常情况下,K 表示键,V 表示值,E 表示异常或错误,T 表示一般意义上的数据类型。当然类型参数只能表示引用类型,而不能表示基本类型,包括int,Double等,但是我们编写程序的时候不会报错,因为程序会对他进行装箱成对应的包装类。例如int--》Integer.
泛型方法和接口
当然泛型还有这么一个用途,就是泛型方法,也就是说我们不用把泛型限制在类上,在接口上,在方法上都是可以得。
public class Demo {
public static void main(String[] args){
// 实例化泛型类
Point<Integer, Integer> p1 = new Point<Integer, Integer>();
p1.setX(10);
p1.setY(20);
p1.printPoint(p1.getX(), p1.getY());
Point<Double, String> p2 = new Point<Double, String>();
p2.setX(25.4);
p2.setY("东京180度");
p2.printPoint(p2.getX(), p2.getY());
}
}
// 定义泛型类
class Point<T1, T2>{
T1 x;
T2 y;
public T1 getX() {
return x;
}
public void setX(T1 x) {
this.x = x;
}
public T2 getY() {
return y;
}
public void setY(T2 y) {
this.y = y;
}
// 定义泛型方法
public <T1, T2> void printPoint(T1 x, T2 y){
T1 m = x;
T2 n = y;
System.out.println("This point is:" + m + ", " + n);
}
}
运行结果:
This point is:10, 20
This point is:25.4, 东京180度
泛型擦除
如果在使用泛型时没有指明数据类型,那么就会擦除泛型类型,请看下面的代码:
public class Demo {
public static void main(String[] args){
Point p = new Point(); // 类型擦除
p.setX(10);
p.setY(20.8);
int x = (Integer)p.getX(); // 向下转型
double y = (Double)p.getY();
System.out.println("This point is:" + x + ", " + y);
}
}
class Point<T1, T2>{
T1 x;
T2 y;
public T1 getX() {
return x;
}
public void setX(T1 x) {
this.x = x;
}
public T2 getY() {
return y;
}
public void setY(T2 y) {
this.y = y;
}
}
这里还有一个小技巧,那就是extends来限制返回值的类型。代码如下:
public <T extends Number> T getMax(T array[]){
T max = null;
for(T element : array){
max = element.doubleValue() > max.doubleValue() ? element : max;
}
return max;
}
<T extends Number> 表示 T 只接受 Number 及其子类,传入其他类型的数据会报错。这里的限定使用关键字 extends,后面可以是类也可以是接口。但这里的 extends 已经不是继承的含义了,应该理解为 T 是继承自 Number 类的类型,或者 T 是实现了 XX 接口的类型。
说了这么多,大家对泛型的认识和使用应该有一个大概的了解吧,其实泛型就是对我们的参数尽心了一个封装,尽量让我们的代码重用性提高。