再看泛型

再看泛型

零 前言

在分析Apache common包的时候发现大量的泛型的较为复杂的应用,有必要对泛型重新探究一下。

一 什么是泛型以及为什么要使用泛型?

听过泛泛之谈,这里的泛型就可以理解为泛泛之类型,即保存的类型是不确定的,但是一旦保存却能记住这种类型,从而帮助编译器在编译阶段就发现错误,这样从另一个方面来讲,也保证了不需要使用强转。这里什么是泛型和为什么要用泛型只说大概,因为要了解一个它是什么(what)和为什么(why), 可能要通过它的工作机制(how)来说明和体现。

不使用泛型:

		Map map1 = new HashMap();
		map1.put("String1","String1");
		map1.put(1,1);
		System.out.println("无泛型的map1"+map1);

使用泛型,并且把Key设置String类型,把Value设置为Integer类型,因为这里的HashMap构造中使用了泛型。如下:

		Map<String,Integer> map2 =  new HashMap<String, Integer>();
		map2.put("String2",2);
		/*我们发现在编译阶段就发现了这个错误*/
//		map2.put("String22","22");
		System.out.println("有泛型的map2"+map2);

二 如何使用泛型

2.1 上面的例子中已经用到了泛型,它是知道要存储的数据是什么类型(String和Integer),然后构造Map(实际上用到的构造函数是Map<K,V>),这样就不用强转了,同时编译器还能够给予提示

2.2 如果我们并不知道存储的是什么类型,又希望编译器帮你保证放入数据的正确性(报错),也能够避免强转,那么也需要使用无限制的通配符。

不使用通配符:

	getMap1(map1);
	public static void getMap1(Map map){
		/*这里是允许的*/
		map.put(1,2);
	}

使用通配符:

	getMap2(map2);
	public static void getMap2(Map<?,?> map){
		/*这里是不允许的*/
//		map.put(1,2);
	}

但是我们发现apache common包中单独使用?这个无限制的通配符的场景并不多见,因为不知道其中存储的类型,所以不能存数据,而在取数据的时候也要把取出的值赋给Object对象。

2.3  ? extend和? super

如果在传参的时候,希望传入是一个类的基类和它的子类,那么就要用到? extend,这种情况下不能添加数据,能读取数据:

	public static void  getList(List<? extends User2Interface> list){
		/*不能添加数据,因为不确定所存放的是User2Interface,还是它的子类*/
		list.add(new User2Interface());
		/*可以取数据*/
		User2Interface user2Interface = list.get(0);
	}

如果在传参的时候,希望传入是一个类的基类和的父类,那么就要用到? super,这种情况下不能取数据,能添加数据:

	public static void  getList(List<? super User2Interface> list){
		/*能添加数据*/
		list.add(new User2Interface());
		/*不可以取数据,因为不确定所存放的是User2Interface,还是它的父类*/
		User2Interface user2Interface = list.get(0);
	}

当然,上面的情形都是可以用Object来接收的。? extend被称为上界通配符,因为它定义了上界,并且由于主要是用来取其中数据,就像从生产者取数据一样,所以适合用来获取生产者的数据;? super被称为下界通配符,因为它定义了下界,并且由于主要是用来往其中添加数据,就想给消费者数据一样,所以适合用来添加消费者的数据。这种应用情形也被称为PECS(Producer Extends Consumer Super)。

另外,如果一个列表既是生产者又是消费者,就不能使用通配符来声明列表,而应该声明具体的类型,如List<Integer>。

2.4 所有的?都可以用泛型符号如T来代替

2.4.1 上句命题是对的,但是反过来却不成立。 因为泛型(如T)的设计之初就是当?满足不了需求的时候。 在什么时候会无法满足需求呢?

当参数之间需要依赖的时候,如:

	public static <T> void getList(List<T> list,Map<T,String> map){
		
	}

上面的代码保证了传入的List的类型,和Map的键值是同一个类;如果采用?,那么就不能保证,即传入的实参可以是不同类型。

当参数和函数的返回值有依赖的时候,如:

	public static <T> List<T> getList(List<T> list){
		return list;
	}

综上,如果能满足需求,尽量使用通配符?,如果不能,再使用泛型符号。这也是JDK文档的建议。

2.4.2 关于泛型的符号的问题,我们常看到的有T,S,R,U,V,K等。而实际上你可以使用任意的英文字符,这点JDK文档并没有要求; 但是有一些默守的规则,如T代表一般的类,但一个T不够用的时候,推荐使用与T相近的字符R,S,U,V等,当使用Map的时候,推荐K(key),V(value)。

2.4.3 关于为什么推荐使用通配符?,有原因的,我们猜测下,?会让代码显得更简洁一点,如下:

		  public static <K,V> getMap3(Map<K,V> map){
		  }
		  public static getMap3(Map<?,?> map){
		  }

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值