java泛型

本文总结java中泛型概念及其用法。java泛型技术在集合框架中应用广泛,学习及总结时梳理了主要内容进行描述,一些零散的泛型使用注意事项体现在代码。

泛型
jdk1.5版本开始出现的新特性,用于解决安全问题,是一个类型安全机制。
jdk1.5中,还可以按原来的方式将各种不同类型的数据装到一个集合中,但编译器会报unchecked警告。

使用泛型的好处
1. 将运行时期出现的ClassCastException问题,转换到了编译时期,方便程序员解决问题,让运行期间问题减少,安全。
2. 避免了类型强制转换麻烦。
没有泛型之前,函数/方法扩展兼容性时一般将形参定义为Object类型,以接收作一类型,而Object类型参数在使用时一般需进行强制类型转换。

泛型格式
通过<>定义要操作的引用数据类型,只要见到<>,就是定义泛型,<>就是用来接收类型的。

泛型常用术语
ArrayList<E>类定义和ArrayList<Integer>类引用中涉及如下术语:
1. 整个称为ArrayList<E>泛型类型

2. ArrayList<E>中E称为类型变量或类型参数
3. 整个ArrayList<Integer>称为参数化的类型
4. ArrayList<Integer>中的Integer称为类型参数的实例或实际参数类型
5. ArrayList<Integer>中的<>念typeof
6. ArrayList称为原始类型

定义带泛型的变量时,有一些注意事项,演示在下面在代码行和注释中:

import java.util.Collection;
import java.util.Vector;

public class GenericDemo1 {
	public static void main(String[] args) {
		//参数化类型可以引用一个原始类型的对象,编译报警告
		Collection<String> collection3=new Vector();
		//原始类型可以引用一个参数化类型对象,编译报警告
		Collection collection4=new Vector<String>();
		Vector<Integer> v1=new Vector<Integer>();
		/*参数化类型不考虑类型参数的继承关系,下面2句编译失败
		 Vector<Stirng> v2=new Vector<Object>();
		 Vector<Object> v3=new Vecotr<String>();
		 Vector<Object> v3=v1;*/

		/*创建数组实例时,数组的元素不能使用参数化的类型,下面语句中vectorLIst是一个数组,数组中的元素不能是Vector<Integer>类型的对象
		Vector<Integer> vectorList[]=new Vector<Integer>[10];*/	
	}
}

泛型在集合框架中很常见,使用集合时,将集合中要存储的数据类型作为参数传递到<>中即可。

import java.util.*;

public class GenericMapDemo {

	public static void main(String[] args) {
		HashMap<String,Integer> maps=new HashMap<String,Integer>();
		maps.put("liso", 23);
		maps.put("wangwu", 25);
		maps.put("zhaoliu",40);
		Set<Map.Entry<String, Integer>> entrySet=maps.entrySet();
		for(Map.Entry<String, Integer> entry:entrySet){
			System.out.println(entry.getKey()+" : "+entry.getValue());
		}
		System.out.println("===========================");
		//遍历集合的另一种方式
		Set<String> kSet=maps.keySet();
		Iterator<String> it=kSet.iterator();
		while(it.hasNext()){
			String str=it.next();
			System.out.println(str+" : "+maps.get(str));
		}
	}
}

泛型除了可以用在集合或数组变量上,还可以用在类和方法上。
泛型方法
为了让类中的不同方法可以操作不同类型,且类型还没确定,可以将泛型定义在方法上。
泛型定义在方法上时,放在返回值之前,修饰符之后。
普通方法、构造方法和静态方法中都可以使用泛型。

泛型方法的使用方法及一些注意事项见下面的代码:

import java.io.Serializable;
class GenericMethod {
	public <T> void show(T t){
		System.out.println("show: "+t);
	}
	public <E> void print(E e){
		System.out.println("print: "+e);
	}
	public <T> T add(T x,T y){
		//return (T)(x+y);编译不通过
		return null;
	}
	//给定任一引用类型的数组,交换其中的元素
	public <T> void swap(T[] a,int i,int j){
		T tmp=a[i];
		a[i]=a[j];
		a[j]=tmp;
	}
	//定义泛型方法时也可以使用extends、super限定符来限定边界
	public <T extends Number> T addNumber(T a,T b){
		return null;
	}
	//可以限定多个边界
	public <V extends Serializable & Cloneable> void method(){
		
	}
	//可以用类型变量表示异常,称为参数化的异常,可以用于方法的throws列表中,但不能用于catch子句中
	private static <T extends Exception> void sayHello() throws T
	{
		try{
			
		}
		catch(Exception e){//不能写成catch(T e)
			throw (T)e;
		}
	}
	//泛型中可以同时有多个类型参数,在定义它们的尖括号中用,分开
	public static <K,V> V getVal(K key){
		return null;
	}	
}
public class GenericMethodDemo{
	public static void main(String[] args){
		GenericMethod gm=new GenericMethod();
		//调用泛型方法时可以随意传入任意类型的参数
		gm.print("haha");
		gm.print(4);
		gm.show(56);
		
		//泛型类型推断
		Number n=gm.add(3.5, 5);//基本数据类型自动装箱成引用类型数据,
		//Double d=gm.add(3.5, 5);//编译不通过,此时的实际类型参数必须是Double和Integer的共同父类,java编译器会进行类型推断来确定和检查类型参数
	    Object obj=gm.add(8, "abd");//Integer和String类型的交集类型是Oject
	    
	    gm.swap(new String[]{"abc", "bdg", "iesdkl"},1,2);
	    //gm.swap(new int[]{2, 4, 5,9},3,1);编译不通过,实际类型参数不能是基本数据类型
	    
	    //addNumber()只能接收Number类及其子类类型对象
	    gm.addNumber(4, 6);
	    gm.addNumber(6.7,9);	    
	    //gm.addNumber("abd", "u");编译不通过
	}
}

泛型类
当类中要操作的引用数据类型(不能是基本数据类型)不确定时,早期定义Object来完成扩展,现在定义泛型来完成扩展。
泛型类定义的泛型,在整个类中有效。泛型类对象中的具体类型确定后,对象就只能操作这一个类型。
注:类中只有一个方法需要使用泛型时,应该使用类级别的泛型,而不是方法级别的泛型。

泛型类的使用方法及一些注意事项见下面的泛型类示例:

//泛型类
class GenericDao<T> {
	private T field;
	public void save(T obj){
		this.field=obj;
	}
	public T getField(){
		return field;
	}
	/*编译不通过,静态方法不可以访问类上定义的泛型,因为静态方法先于泛型对象加载
	public static T update(T obj){
		
	}*/
	//静态方法操作的引用数据类型不确定时,可以将泛型定义在方法上
	public static <T> T update(T obj){
		return obj;
	}
	//泛型类中还可以定义 泛型方法,泛型方法使用的类型与泛型类中的可以不相同
	public <E> void display(E e){
		System.out.println(e);
	}
}
public class GenericDaoTest{
	public static void main(String[] args){
		GenericDao<Integer> g=new GenericDao<Integer>();
		//编译不通过,泛型类的类型参数化时,类型参数的实例必须是引用类型,不能是基本类型
		//GenericDao<int> gg=new GenericDao<int>();
		g.save(8);
		System.out.println(g.getField());
		g.display("hehe");
		System.out.println(GenericDao.update("haha"));
		//编译不通过
		//g.save("aaa");
	}
}

泛型的通配符
类型参数?表示可接收任一泛型
? extends E:可以接收E类型或者E类型的子类型
? super E:可以接E类型或E类型的父类型

泛型通配符使用方法及注意事项演示在下面的代码中:

import java.util.*;

public class GenericDemo4 {

	public static void main(String[] args) {
		//泛型中的通配符基本用法
		//Integer是Number类的子类
		Vector<? extends Number> v4=new Vector<Integer>();
		//Vector<? extends Number> v5=new Vector<Object>();//编译不通过
		Vector<? super Integer> v6=new Vector<Number>();
		//Vector<? super Integer> v7=new Vector<Byte>();编译不通过
		Class<?> y;
		Class<String> x=String.class;
		y=x;
		//x=y;编译不通过,任一个类型泛型对象不能赋给指定类型泛型对象
	}
	public static void printCollection(Collection<?> collection){
		//方法体中不能调用与参数化有关的方法,因为不知道自己未来匹配的类型是不是String
		//collection.add("afb");
		collection=new ArrayList<String>();//没错,可以整体指定泛型参数实例。
		//可以调用与参数化无关的方法
		System.out.println(collection.size());
	}
}
java泛型原理

java中的泛型类型(或者泛型)类似于C++中的模板,但这种类似性仅限于表面,java语言中的泛型基本上完全是在编译器中实现,用于给编译器执行类型检查和类型推断,然后生成普通的非泛型的字节码,这种实现技术称为擦除(erasure)(编译器使用泛型类型信息保证类型安全,然后在生成字节之前将其清除)。这是因为扩展虚拟机指令集来支持泛型被认为是无法接受的,这会为java厂商升级其jvm造成难以逾越的障碍。所以java的泛型采用了完全可以在编译器中实现的擦除方法。

泛型是提供给java编译器使用的,让编译器挡住源程序中的非法输入;已参数化的泛型类型,getClass方法的返回值与原始类型完全一样,编译器编译完成后,生成的字节码中会去掉泛型的类型信息,使程序运行效率不受影响。

只要能跳过编译器,就可以往某个泛型集合中加入其它类型的数据,例如,用反射得到集合,再调用add()方法即可。

利用反射还可以得到泛型的实际类型参数:

import java.lang.reflect.*;
import java.util.*;

public class GenericDemo3 {
	public static void main(String[] args) throws Exception
	{
		ArrayList<String> collection1=new ArrayList<String>();
		ArrayList<Integer> collection2=new ArrayList<Integer>();
		System.out.println(collection1.getClass()==collection2.getClass());//true
		//利用反射技术向实际类型参数为Integer的泛型类型对象中添加String字符串
		collection2.getClass().getMethod("add", Object.class).invoke(collection2, "abc");
		System.out.println(collection2.get(0));//打印集合的第1个元素"abc"
		
		//利用反射技术获取泛型的实际类型参数
		Method applyMethod=GenericDemo3.class.getMethod("applyVector", Vector.class);
		//获取Method对象的所有形参类型,该方法在jdk1.5版本才有
		Type[] types=applyMethod.getGenericParameterTypes();
		//获取第1个形参类型,ParameterizedType是一个接口,表示参数化类型,如 Collection<String>
		ParameterizedType pType=(ParameterizedType)types[0];
		//获取参数的类型
		System.out.println(pType.getRawType());
		//返回泛型的实际类型参数
		System.out.println(pType.getActualTypeArguments()[0]);
	}
	public static void applyVector(Vector<Date> v1){
		
	}	
}
/*运行结果:
 true
abc
class java.util.Vector
class java.util.Date
*/





  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值