本次学习的目标
掌握泛型的产生异议
掌握泛型的基本使用
了解泛型的警告信息及泛型的擦除
具体内容
1.为什么要使用能够泛型(Generic)?
首先,我们来设计一个实例,要求如下:
现在要求设计一个可以表示出坐标点的类,坐标有X和Y组成,坐标的表示方法有一下三种
整数表示:x = 10、y = 20
小数表示:x = 10.5、y = 20.5
字符串表示:x =“东经120度”、y =“北纬26度”
那么这个类要怎么设计呢?
我们来分析一下,这样的要求,首先要考虑到,必须建立好一个表示坐标点的类——Point,此类中有两个属性分别用来报表时x坐标和y坐标,但是x和y中所保存的数据类型会有三种(int、float、String),而要想使用一个类型可以同时接受这样的三种类型数据,则现在只能使用Object,因为Object类可以接受任何类型的数据,都会自动发生向上转型操作,这样三种数据类型将按以下的方式进行转化:
数字(int)-->自动装箱成Integer-->向上转型使用Object接收
小数(float)-->自动装箱成Float-->向上转型使用Object接收
字符串(String)-->向上转型使用Object接收(这里没有装箱操作是因为Strings是Object的子类)
下面我们新建项目GenericDemo,再新建类GenericDemo01、GenericDemo02、GenericDemo03
classPoint{
privateObjectx;//表示X坐标
privateObjecty;//表示Y坐标
publicvoidsetX(Object x){
this.x= x;
}
publicvoidsetY(Object y){
this.y= y;
}
publicObject getX(){
returnthis.x;
}
publicObject getY(){
returnthis.y;
}
}
publicclassGenericDemo01{
publicstaticvoidmain(String args[]){
Pointp =newPoint() ;//声明一个Point的对象
p.setX(10);//利用自动装箱操作:int--> Integer --> Object
p.setY(20);//利用自动装箱操作:int--> Integer --> Object
intx =(Integer)p.getX() ;//取出数据先变为Integer,之后自动拆箱
inty =(Integer)p.getY() ;//取出数据先变为Integer,之后自动拆箱
}
}
运行结果:
publicclassGenericDemo02{
publicstaticvoidmain(String args[]){
Pointp =newPoint() ;//声明一个Point的对象
p.setX(10.5f);//利用自动装箱操作:float --> Float --> Object
p.setY(20.6f);//利用自动装箱操作:float --> Float --> Object
floatx =(Float)p.getX() ;//取出数据先变为Integer,之后自动拆箱
floaty =(Float)p.getY() ;//取出数据先变为Integer,之后自动拆箱
}
}
运行结果:
publicclassGenericDemo03{
publicstaticvoidmain(String args[]){
Pointp =newPoint() ;//声明一个Point的对象
p.setX("东经120度") ;// String --> Object
p.setY("北纬26度") ;// String --> Object
Stringx = (String)p.getX() ;//取出数据先变为Integer,之后自动拆箱
Stringy = (String)p.getY() ;//取出数据先变为Integer,之后自动拆箱
}
}
运行结果:
因为这三个类在同一个项目的同个包下,Point可以直接调用。
可以看到三个数据类型的输出结果。
我们这样一看,好像程序都是正确的,结果也输出了,其实上面的代码也存在着很大的问题,现在假设有一下的程序代码。
publicclassGenericDemo04{
publicstaticvoidmain(String args[]){
Pointp =newPoint() ;//声明一个Point的对象
p.setX(10);//利用自动装箱操作:int--> Integer --> Object
p.setY("北纬26度") ;// String --> Object
int x =(Integer)p.getX() ;//取出数据先变为Integer,之后自动拆箱
int y =(Integer)p.getY() ;//取出数据先变为Integer,之后自动拆箱
}
}
这样的代码,Int类型能输出String类型的结果吗?肯定是不能的,所以我们上面传统的实现方法就有可能出现这种操作不当的情况。数据类型不统一就会造成一下运行结果:
所以为了解决这个问题,我们就引入了泛型。
2.认识泛型
泛型可以解决数据类型的安全性问题,它主要的原理,是在类声明的时候通过一个标识标识类中某个属性的类型或者是某个方法的返回值及参数类型,这样在类声明或实例化的时候,只要指定好需要的类型即可。
泛型类定义格式:
访问权限class类名称{
访问权限泛型类型标识变量名称;
访问权限泛型类型标识方法名称(){};
访问权限返回值类型声明方法名称(泛型类型标识变量名称){};
}
泛型对象定义
类名称对象名称= new类名称();
下面我们按照此格式定义一个Points类,新建GenericDemo05
classPoints{//此处可以随便写标识符号,T是type的简称
privateTvar;//var的类型由T指定,即:由外部指定
publicTgetVar(){//返回值的类型由外部决定
returnvar;
}
publicvoidsetVar(T var){//设置的类型也由外部决定
this.var=var ;
}
}
publicclassGenericDemo05{
publicstaticvoidmain(String args[]){
Pointsp =newPoints() ;//里面的var类型为String类型
p.setVar("ABC") ;//设置字符串
System.out.println(p.getVar().length()) ;//取得字符串的长度
}
}
运行结果:
输出字符串长度
以上我们是将var变量设置成了String类型,当然,也可以设置成Integer,如果设置的内容与指订的泛型类型不一致,就会在编译时出现错误。
publicclassGenericDemo06{
publicstaticvoidmain(String args[]){
Pointsp =newPoints() ;//里面的var类型为String类型
p.setVar("ABC") ;//设置字符串
}
}
报错:
所以这样可以更好的保护数据类型。
通过泛型就可以直接修改上面的程序。
classPoint1{
privateTx;//表示X坐标
privateTy;//表示Y坐标
publicvoidsetX(T x){
this.x= x;
}
publicvoidsetY(T y){
this.y= y;
}
publicTgetX(){
returnthis.x;
}
publicTgetY(){
returnthis.y;
}
}
publicclassGenericPoint{
publicstaticvoidmain(Stringargs[]){
Point1p =newPoint1() ;
p.setX(10);//利用自动装箱操作:int--> Integer
p.setY(20) ;//利用自动装箱操作:int--> Integer
intx =p.getX() ;//自动拆箱
inty =p.getY() ;//自动拆箱
}
}
运行结果:
在这样的程序里,就减少了类型转换的操作代码,而且更安全,如果设置的内容不是数字,则在编译的时候讲出现错误。
p.setY("北纬26度") ;
泛型也可以在构造方法中使用,一般有可能使用构造方法为类中的属性赋值。
3.构造方法中使用泛型
构造方法可以为类中的属性初始化,那么如果类中的属性通过泛型指定,而又需要通过构造设置属性内容的时候,那么构造方法的定义与之前并无不同,不需要想声明类那样指定泛型。
使用格式:
访问权限构造方法(参数名称){}
我们新建GenericDemo07
classPoint2{//此处可以随便写标识符号,T是type的简称
privateTvar;//var的类型由T指定,即:由外部指定
publicPoint2(T var){//通过构造方法设置内容
this.var=var ;
}
publicTgetVar(){//返回值的类型由外部决定
returnvar;
}
publicvoidsetVar(T var){//设置的类型也由外部决定
this.var=var ;
}
}
publicclassGenericDemo07{
publicstaticvoidmain(String args[]){
Point2p =newPoint2("XQ") ;//里面的var类型为String类型
}
}
运行结果:
输出结果,当然我们在泛型中也可以同时指定多个泛型类型。
4.设置多个泛型
新建GenericDemo08
classNotepad{//此处指定了两个泛型类型
privateKkey;//此变量的类型由外部决定
privateVvalue;//此变量的类型由外部决定
publicKgetKey(){
returnthis.key;
}
publicVgetValue(){
returnthis.value;
}
publicvoidsetKey(K key){
this.key=key ;
}
publicvoidsetValue(V value){
this.value=value ;
}
}
publicclassGenericDemo08{
publicstaticvoidmain(String args[]){
Notepadt =null;//定义两个泛型类型的对象
t=newNotepad() ;//里面的key为String,value为Integer
t.setKey("张三") ;//设置第一个内容
t.setValue(30);//设置第二个内容
System.out.print("姓名;"+t.getKey()) ;//取得信息
System.out.print(",年龄;"+t.getValue()) ;//取得信息
}
}
运行结果:
5.泛型的安全警告
在泛型应用中最好在声明类对线过的时候指定好其内部的数据类型,例如:“Info”,但是也可以不指定类型,这样一来用户在使用这样的类时,就会出现不安全的警告信息。
新建GenericDemo09
classInfo{
privateTvar;
publicTgetVar(){
returnthis.var;
}
publicvoidsetVar(T var){
this.var=var ;
}
publicString toString(){//覆写Object类中的toString()方法
}
}
publicclassGenericDemo09{
publicstaticvoidmain(String args[]){
Infoi =newInfo() ;//警告,没有指定泛型类型
i.setVar("ABC");//设置字符串
}
}
这里可以看到有安全警告,说在Info类中没有指定泛型的类型,则在java中为了保护程序依然可以使用,会将T设置成Object类型,这样一来,就可以接收任意的数据类型,也就是说此时var的类型就是Object,所有的泛型信息将会被擦除,我们运行后还是可以正常输出。
实际上以上的程序就相当于一下的定义格式:
Info i =new Info() ;
总结
1、泛型的产生意义:为了保证数据的安全性
泛型的基本使用,由外部指定其具体的操作类型。
关爱IT人,关注挨踢栏
领取专属 10元无门槛券
私享最新 技术干货