泛型是一种程序设计手段,使用泛型机制编写的程序代码要比那些杂乱地使用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);
                
	}
}

java 泛型对象new出来_java

将上面的主方法改为:

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);
	}
}

java 泛型对象new出来_java_02

虽然实现了,但是上面的程序有一定的问题,假如有人将主方法写为

p.setX(10);	//向上转型:String-->Object
p.setY("北纬210度");
int x = (Integer)p.getX();	//取出数据后强制类型转换变为String类型
int y = (Integer)p.getY();

java 泛型对象new出来_java 泛型对象new出来_03

这时编译不会出现异常,执行时出现类型转换异常,即传统的实现方法会出现操作不当的情况,此时就需要泛型来解决这个问题,这也就是为什么泛型程序设计会具有更高的安全性

二.泛型的用法

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());
	}
}

java 泛型对象new出来_java_04


在创建泛型类对象时,如果没声明具体泛型类型,编译时会产生安全警告,但是并不会影响执行

我们可以看到创建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());
	}
}

java 泛型对象new出来_基础知识_05

此时并不会出现警告,所以说在泛型应用中最好在声明类的时候指定好其内部的数据类型,例如"Point<String>",如果不声明类型,用户在使用中就会出现不安全的警告信息

java 泛型对象new出来_基础知识_06

参考资料:

java核心技术卷一