泛型详解

泛型详解之

为什么需要学习泛型?

  • 在JDK1.5之前是没有泛型,当时集合能够存储的都是使用父类Object类型作为形参接受所有的类型
  • 如果存储的是同一种类型向下转型没问题,如果集合存储的是不同类型,那么会出现ClassCastException类型转换异常
  • 所以我们在转型之前做类型判断,判断后虽然解决了安全问题,但是又出现了 遍历不完全的情况,所以我们需要针对Object可能出现的子类做逐一判断,判断后解决了遍历不完整和安全问题,但是如果集合又添加了一个新的类型的元素,遍历又不完整,需要继续添加else if语句块,所以违背了开闭原则,不好维护,可以违背开闭原则,但是又因为Object类有无数个子类,所以永远无法解决,所以引入了泛型

泛型是模仿数组和方法的特点:

  • 模仿数组的 编译时期 确定类型
  • 模仿方法的 参数化类型

泛型的概述

泛型是一种独立的技术,和集合无关,是能够将类型在编译时期提前确定的参数化类型

  • 泛型的格式: 这里的E必须是引用数据类型,这里可以A~Z
		Collection <String> c=new ArrayList<String>();
		//集合泛型有传染性在Collection<E>:源码ArrayList会继承<E>,以下省略<E>也正确
		Collection <String> c2=new ArrayList();

泛型的好处:

  • 1.消除了黄色警告线,提高了安全性
  • 2.去除了向下转型,提高了效率
  • 3.满足了开闭原则 (提高了扩展性和可维护性)
  • 4.提高了可读性
  • 5.简化了代
    案例
public static void main(String[] args) {
		Collection <String> c=new ArrayList<String>();
		//集合泛型有传染性在Collection<E>:源码ArrayList会继承<E>,以下省略<E>也正确
		Collection <String> c2=new ArrayList();
		
		c.add("sa");
		//c.add(2);   报错
		Iterator<String> i=c.iterator();
		//迭代器接收c的信息,返回要加<E>的警告,加<E>后消除,且用next接收时,不要向下转型
		while(i.hasNext()){
			String s=i.next();//不用向下转型
			System.out.println(s);
		}
}

泛型类:

class Gener<E,H>{
	private E e;
	private H h;
	public E getE() {
		return e;
	}
	public void setE(E e) {
		this.e = e;
	}
	public H getH() {
		return h;
	}
	public void setH(H h) {
		this.h = h;
	}
}

public static void main(String[] args) {
		// TODO Auto-generated method stub
		Gener<String,Double> g=new Gener();
		g.setE("gg");
		String s=g.getE();
		g.setH(12.5);
		Double d=g.getH();
	}

集合的传染性

public static void main(String[] args) {
		Collection <String> c=new ArrayList<String>();
		//集合泛型有传染性在Collection<E>:源码ArrayList会继承<E>,以下省略<E>也正确
		Collection <String> c2=new ArrayList();
		
		c.add("sa");
		//c.add(2);   报错
		Iterator<String> i=c.iterator();
		//迭代器接收c的信息,返回要加<E>的警告,加<E>后消除,且用next接收时,不用向下转型
		while(i.hasNext()){
			String s=i.next();
			System.out.println(s);
		}
}		

泛型接口

实现接口的三种方式:(个人推荐2、3)

interface Igener<E,H>{
	void method(E e);
	H test(H h);
}
//-----------------------------------1,对应实现1
class generImpl implements Igener<String,Integer>{
	@Override
	public void method(String e) {
		// TODO Auto-generated method stub		
	}
	@Override
	public Integer test(Integer h) {
		// TODO Auto-generated method stub
		return null;
	}	
}
//-----------------------------------2,对应实现2
class generImpl2<E,H> implements Igener<E,H>{
	@Override
	public void method(E e) {
		// TODO Auto-generated method stub	
	}
	@Override
	public H test(H h) {
		// TODO Auto-generated method stub
		return null;
	}
}
		//实现1,注意接口前面的类型一定要定义好,主方法里不能用<E,H>
		Igener<String,Integer> i1=new generImpl();
		//在实现类就确定好了类型,new后面不能有<String,Integer>,否则报错
		//Igener<String,Integer>类型应该和实现类的类型相匹配
		i1.test(12);
		i1.method("123");
		
		//实现2,<E,H>是在main方法中临时写好的,但是方法中会即时编译
		Igener<String,String> i2=new generImpl2<String,String>();
		Igener<String,String> i3=new generImpl2();
		//Igener<String,String>一定要写,new后面的<String,Integer>可写可不写
		i2.method("asd");
		i2.test("as");
		
		//实现3,利用其子类的匿名对象实例化
		Igener<String,Double> i4=new Igener<String,Double>(){
			@Override
			public void method(String e) {
				// TODO Auto-generated method stub				
			}
			@Override
			public Double test(Double h) {
				// TODO Auto-generated method stub
				return null;
			}		
		};//注意别少了分号
		i4.test(12.5);
		i4.method("sa");

泛型方法

泛型方法定义:

把泛型定义在方法上,泛型方法独立于泛型类,又称为局部泛型
泛型方法确定类型的时机是方法调用的时候
泛型方法格式: public T[] toArray(T[] a)

class Ger<E>{//                               此处E是全局泛型
	private E e;

	public E getE() {
		return e;
	}

	public void setE(E e) {//不是泛型方法   因为E是全局泛型,方法需要局部泛型
		this.e = e;
	}
	public <T> void show (T t){                   //泛型方法1
		System.out.println(t.getClass().getName());
	}
	public <K,V> void test(K k,V v){              //泛型方法2
		System.out.println(k);
		System.out.println(k.getClass().getName());
		System.out.println(v);
	}	
}

泛型方法测试代码:
注意:有个很重要的方法:toArray()
写法标记:toArray中是我想要实现的数据类 格式为:new 类名[]{}

public static void main(String[] args) {
		// TODO Auto-generated method stub
		Ger<String> g=new Ger<String>();
		g.setE("as");
		System.out.println(g.getE());
		g.show(8);//          调用泛型方法一,返回对象类名java.lang.Integer
		g.test(false, 8.5);//    调用泛型方法2,返回对象名,对象信息
		//----------------------------------------false
		//java.lang.Boolean
		//8.5
		Collection<Integer> c=new ArrayList<Integer>();
		c.add(1);
		c.add(2);
		c.add(3);
		Integer[] ii1=c.toArray(new Integer[]{} );//记住这种写法,很关键
		//捕获c的元素后给它自己定义的类型来接收,不要向下转型
		//这个方法很强大,不要向下转型!!!!
		for (Integer in : ii1) {//打印深拷贝过来的集合元素
			System.out.println(in);
		}		
	}

泛型限定符

泛型限定符
? : 表示泛型类型可以是任意类型
? extends E: 表示泛型类型可以是 E 或者是 E的子类
? super E: 表示泛型类型可以是E 或者是 E的父类
为什么要用到?
因为泛型的声明和实现规定是要一一对应的,比如:
Collection c=new ArrayList();
两个String要一定相同,所以不能出现以下情况
Collection c=new ArrayList();
Collection c=new ArrayList();
因此,我们用?来实现继承和超类
? extends E: 表示泛型类型可以是 E 或者是 E的子类
? super E: 表示泛型类型可以是 E 或者是 E的父类

定义普通类实现继承

class Father{
	
}
class Son extends Father{
	
}
class Daughter extends Father{
	
}

?的使用方法:
? extends T 可以读取到T对象,而不能写入T对象和T的子对象
? super T 可以读取到Object对象,可以写入T对象和T的子对象

下界<? super T>不影响往里存,但往外取只能放在Object对象里
链接:http://www.imooc.com/article/252935?block_id=tuijian_wz

若想既可以读取又可以写入,则不要用通配符,直接用T

		public static void main(String[] args) {
		// TODO Auto-generated method stub
		Collection<Father> c=new ArrayList<Father>();
		Collection<?> c2=new ArrayList<Object>();
		Collection<? extends Father> c3=new ArrayList<Daughter>();
		Collection<?> c4=new ArrayList<Son>();
		Collection<?extends Father> c5=new ArrayList<Father>();
		Collection<? super Son> c6=new ArrayList<Father>();
		Collection<? super Daughter> c7=new ArrayList<Father>();
		List<? super Father> c8=new ArrayList<Object>();
		//c5.addAll(new ArrayList<Father>());//编译时异常,因为? extends T  只能读取,不能加
		c8.addAll(new ArrayList<Father>());//添加成功
		System.out.println(c8.get(0));//运行时异常  ? 
	}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值