从Java程序的设计语言1.0版本发布以来,变化最大的部分就是泛型。致使Java SE5.0中增加泛型机制的主要原因为了满足在1999年制定的最早的Java规范需求之一(JSR14).专家组花费了5年左右的时间来定义规范和测试实现。泛型正是我们需要的程序设计手段。使用泛型机制编写的代码要比那些杂乱的使用Object变量,然后再进行强制类型转换的代码具有更好的安全性和可读性。泛型对于集合类尤其有用,例如,ArrayList就是一个无处不在的集合类。至少在表面上来看,泛型很像C++的模板。与Java一样,在C++中,模板也就是最先被添加到语言中支持强类型集合的。但是,多年以后人们发现模板还有其它的用武之地。
泛型程序设计意味着编写的代码可以被很多不同类型的对象所重用。例如,我们并不希望为聚集在任何类型的对象。这是一个泛型程序设计的实例。这样做,因为一个ArrayList类可以聚集任何类型的对象。这是一个泛型程序设计的例子。
实际上,在Java增加泛型类之前已经有一个ArrayList类。下面来研究泛型程序设计的机制是如何演变的,另外还会讲解这对于用户和实现者来说意味着什么。
在Java语言中,为了方便接收参数类型的统一,提供了一个核心类Object,利用此类型对象可以接收所有类型的数据。但是由于其所描述的数据范围过大,所以在实际使用中就会出现传入数据类型错误,从而引发ClassCastExeption异常。例如:现在要设计一个可以描述坐标点的类point,对于坐标点允许保存三种数据。
整型数据浮点型数据字符串型数据整型数据浮点类型数据字符串型数据point类中的x与y属性都采用了Object作为存储类型,这样就可以接受任何任意的数据类型,于是此时就可能产生两种情况。
程序运行的结果:x的坐标:100、y坐标:20
这个程序在设置Point类坐标数据时采用了不同的数据类型,所以在获取原始数据信息时就会出现程序运行的异常,即这类错误并不会在编译的时候告诉开发者,而是在执行过程中才会产生安全隐患,而造成此问题的核心原因就是Object类型能够接受的数据范围过大。
泛型可以在编译时检测出程序的安全隐患,使用泛型技术可以使程序更加健壮。
如果要想解决项目中的可能出现的ClassCastExeption安全隐患,最为核心的方案就是避免强制性的进行对象向下转型操作。所以泛型设计的核心思想在于:类中的属性或方法的参数与返回值的类型采用动态标记:在对象实例化的时候动态配置要使用的数据类型。 ###例子:在类定义上使用泛型
public 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 void getX(){ return x; } public void getY(){ return y; } public class Demo3{ public static void main(Stribg args[]){ Point<Integer> point=new Point<Integer>(); point.setX(100); point.setY(200); int x=point.getX(); int y=point.getY(); } } }程序运行结果:x的坐标:100、y的坐标:200
在Java中增加泛型类之前,泛型程序设计是用继承实现的。ArrayList类只维护一个Object引用的的数组
public class ArryList { private Object[] elementData; .... public Object get(int i){....} public void add(Object o){......} } 这种方法有两个问题。当获取一个值时候必须进行强制类型转换。 ArrayList files=new ArrayList(); ..... String filename=(String) file.get(0); 此外,这里没有错误类型检查。可以向数组列表中添加任何类的对象。 files.add(new File("..."); 对于这个调用,编译和运行都不会出错。然而,在其他地方,如果将get的结果强制类型转换为String类型,就会产生一个错误。 泛型提供了一个更好的解决方案:类型参数(type parameters)。ArrayList类有一个类型参数用来指示元素的类型。 ArrayList<String> files=new ArrayList<String>();这使得代码具有更好的可读性。人们一看就知道这个数组列表中包含的是String对象。
前面已经提到,在Java SE7及以后的版本中,构造函数中可以省略泛型类型:
ArrayList<String> fiels=new ArrayList<>();省略的类型可以从变量的类型推断得出
编译器也可以更好的利用这个信息。当调用get的时候,不需要进行强制类型转换,编译器就知道返回值的类型为String,而不是Object:
String filename=files.get(0); 编译器还知道ArrayList中add方法有一个类型为String的参数。这将比使用Object类型的参数安全一些。现在,编译器可以进行检查,避免插入错误类型的对象。例如: files.add(new File("..."));是无法通过编译的。出现编译错误比类在运行时出现类的强制类型转换异常要好很多。 类型参数的美丽在于:使得程序具有更好的可读性和安全性。
泛型除了定义在类上也可以定义在接口上,这样的结构成为泛型接口。
对于此事的IMessage泛型接口在进行子类定义时就有两种实现方式:在子类中继续声明泛型和子类中为父类设置为泛型。
运行结果:[echo]Java编程中心
在一定的环境下,类与接口往往不需要进行泛型定义,然而对于该结构体中的方法又可能出现泛型要求。
##泛型总结:
1.泛型具有参数化的能力。可以定义使用泛型类型的类或方法,编译器会用具体类型来替换泛型类型。2.泛型的主要优势是能够在编译时而不是运行时检查错误。3.泛型类或方法允许指定这个类或方法可以带有的对象类型。如果试图使用带有不兼容对象的类或方法,编译器会检测出这个错误。4.定义在类、接口或者静态方法中的泛型称为形式泛型类型,随后可以使用一个实际具体类型来替换它。替换泛型类型的过程称为实例化。5.不使用类型参数的泛型类称为原始类型,例如ArrayList。使用原始类型是为了向后兼容Java较早的版本。6.通配泛型类型有三种形式:?、?extends T和?super T,这里的T代表一个泛型类型。第一种形式?称为非受限通配,它和?extends Object是一样的。第二种形式?extends T称为受限通配,代表T或者T的一个子类型。第三种类型?super T称为下限通配,表示或者T的一个父类型。7.使用称为类型消除的方法来实现泛型。编译器使用泛型类型信息来编译代码,但是随后消除它。因此,泛型信息在运行是不可用的。这个放啊发能够使泛型代码向后兼容使用原始;类型的遗留代码。8.不能使用泛型类型参数来创建实例9.不能使用泛型类型参数创建数组10.不能再静态实例中使用类的泛型参数11.在异常类中不能使用泛型类型参数。文章参考有:《Java从入门到项目实战》、《Java核心技术卷》
