泛型是一种程序设计手段,使用泛型机制编写的程序代码要比那些杂乱地使用Obeject变量,然后再进行强制类型转换的代码具有更好的安全性和可读性
一.为什么要使用泛型程序设计
泛型程序设计(Generic Programming)意味着编写的代码可以被很多不同类型的对象所重用
例:
设计一个可以表示坐标点的类,坐标由x和y组成,表示方法如下:
整数表示:x=10,y=20
字符串表示:x=“东经180度”,y=“北纬210度”
如何设计该类?
分析:建立一个叫做Point的类,类中有x、y两个属性。只是这两个属性有两种数据类型(int,String),要想同时接收这样的两种数据,只能使用Object类,因为Object类可以接收任意数据类型的数据,都会发生向上转型操作:
int-->自动装箱成Integer-->向上转型使用Obeject接收
String-->向上转型使用Object接收
所以我们可以设计出以下程序:
class Point{
private Object x; //表示x坐标
private Object y; //表示y坐标
public void setX(Object x){
this.x = x;
}
public void setY(Object y){
this.y = y;
}
public Object getX(){
return this.x;
}
public Object getY(){
return this.y;
}
}
public class GenericDemo01{
public static void main(String[] args){
Point p = new Point();
p.setX(10); //利用自动装箱操作:int-->Integer
p.setY(20);
int x = (Integer)p.getX(); //取出数据后先变为Integer类型,然后自动拆箱
int y = (Integer)p.getY();
System.out.println("整数表示,x坐标为:"+x);
System.out.println("整数表示,y坐标为:"+y);
}
}
将上面的主方法改为:
public static void main(String[] args){
Point p = new Point();
p.setX("东经180度"); //向上转型:String-->Object
p.setY("北纬210度");
String x = (String)p.getX(); //取出数据后强制类型转换变为String类型
String y = (String)p.getY();
System.out.println("字符串表示,x坐标为:"+x);
System.out.println("字符串表示,y坐标为:"+y);
}
}
虽然实现了,但是上面的程序有一定的问题,假如有人将主方法写为
p.setX(10); //向上转型:String-->Object
p.setY("北纬210度");
int x = (Integer)p.getX(); //取出数据后强制类型转换变为String类型
int y = (Integer)p.getY();
这时编译不会出现异常,执行时出现类型转换异常,即传统的实现方法会出现操作不当的情况,此时就需要泛型来解决这个问题,这也就是为什么泛型程序设计会具有更高的安全性
二.泛型的用法
1.泛型类
一个泛型类就是具有一个或多个类型变量的类
泛型类定义格式:
class 类名称<泛型类型,泛型类型,...>{}
具体:
[访问权限]class 类名称<泛型类型1,泛型类型2,...>{
[访问权限]泛型类型标识 变量名称;
[访问权限]泛型类型标识 方法名称(){};
[访问权限]返回值类型声明 方法名称(泛型类型标识 变量名称){};
}
泛型对象定义:
类名称<具体类> 对象名称 = new 类名称<具体类>();
class Point<T>{
private T var; //var的类型由T指定,即:由外部指定
public T getVar(){ //返回值类型由安慰、外部决定
return var;
}
public void setVar(T var){ //设置类型也由外部决定
this.var = var;
}
}
public class GenericDemo03{
public static void main(String[] args){
Point<String> p = new Point<String>();
p.setVar("Jack");
System.out.println(p.getVar());
}
}
p.setVar("Jack")是用来设置内容的,如果设置的内容与指定的泛型类型不一致,则在编译时将出现错误,可以更好的保护数据类型
利用泛型将例子写出来如下:
class Point<T>{
private T x; //表示x坐标
private T y; //表示y坐标
public void setX(T x){
this.x = x;
}
public void setY(T y){
this.y = y;
}
public T getX(){
return this.x;
}
public T getY(){
return this.y;
}
}
public class GenericDemo04{
public static void main(String[] args){
Point<Integer> p = new Point<Integer>();
p.setX(10); //利用自动装箱操作:int-->Integer
p.setY(20);
//int x = (Integer)p.getX(); //取出数据后先变为Integer类型,然后自动拆箱
//int y = (Integer)p.getY();
int x = p.getX();
int y = p.getY();
System.out.println("整数表示,x坐标为:"+x);
System.out.println("整数表示,y坐标为:"+y);
}
}
这里明显看出,程序中了少了类型强制转换的部分,而且更加安全,如果设置的内容不是int类型,则在编译时会报错
除此之外在泛型中可以同时指定多个泛型类型:
class Point<K,V>{
private K key;
private V value;
public Point(K key,V value){
this.setKey(key);
this.setValue(value);
}
public K getKey(){
return this.key;
}
public V getValue(){
return this.value;
}
public void setKey(K key){
this.key = key;
}
public void setValue(V value){
this.value = value;
}
public String getInfo(){
return "姓名:"+key+"\t年龄:"+value;
}
}
public class GenericDemo06{
public static void main(String[] args){
Point<String,Integer> p = new Point<String,Integer>("Jack",30);
System.out.println(p.getInfo());
}
}
在泛型类的构造方法中使用泛型
public Point(T var){}
class Point<T>{
private T var; //var的类型由T指定,即:由外部指定
public Point(T var){
this.setVar(var);
}
public T getVar(){ //返回值类型由安慰、外部决定
return var;
}
public void setVar(T var){ //设置类型也由外部决定
this.var = var;
}
}
public class GenericDemo05{
public static void main(String[] args){
Point<String> p = new Point<String>("Jack");
System.out.println(p.getVar());
}
}
2.泛型方法
泛型方法中可以定义泛型参数,此时参数的类型就是传入数据的类型,定义如下:
[访问权限] <泛型标识> 泛型标识 方法名称(泛型标识 参数名称){}
class Demo{
public static <T> T fun(T t){
return t;
}
}
public class GenericDemo07{
public static void main(String[] args){
Demo d = new Demo();
String str = d.<String>fun("Jack");
int i = d.<Integer>fun(30);
System.out.println(str);
System.out.println(i);
}
}
fun()方法是在普通类中定义的,当然也可以在泛型类中定义。注意:类型变量放在修饰符(public static)后面,返回类型的前面
当调用一个泛型方法时,在方法名前的尖括号里放入具体的类型,如上String str = d.<String>fun("Jack"),但是实际情况下,方法调用中可以省略<String>参数,因为编译器能够推断出所调用方法的参数类型
public class GenericDemo07{
public static void main(String[] args){
Demo d = new Demo();
String str = d.fun("Jack");
int i = d.fun(30);
System.out.println(str);
System.out.println(i);
}
}
可以看出,如果在方法中定义了泛型,则可以传递任意数据类型
三.泛型的警告信息及泛型的擦除
class Point<T>{
private T var; //var的类型由T指定,即:由外部指定
public Point(T var){
this.setVar(var);
}
public T getVar(){ //返回值类型由安慰、外部决定
return var;
}
public void setVar(T var){ //设置类型也由外部决定
this.var = var;
}
}
public class GenericDemo08{
public static void main(String[] args){
Point p = new Point("Jack");
System.out.println(p.getVar());
}
}
在创建泛型类对象时,如果没声明具体泛型类型,编译时会产生安全警告,但是并不会影响执行
我们可以看到创建Point类对象时并没有指定泛型的类型,即Point类并没有指定泛型的类型,则java中为了保证程序依然可以使用,会将T设置成Object类型,说明此时var的类型就是Object,所有的泛型信息将会被擦除,实际上,以上程序相当于如下代码:
class Point<T>{
private T var; //var的类型由T指定,即:由外部指定
public Point(T var){
this.setVar(var);
}
public T getVar(){ //返回值类型由安慰、外部决定
return var;
}
public void setVar(T var){ //设置类型也由外部决定
this.var = var;
}
}
public class GenericDemo08{
public static void main(String[] args){
Point<Object> p = new Point<Object>("Jack");
System.out.println(p.getVar());
}
}
此时并不会出现警告,所以说在泛型应用中最好在声明类的时候指定好其内部的数据类型,例如"Point<String>",如果不声明类型,用户在使用中就会出现不安全的警告信息
参考资料:
java核心技术卷一