[泛型]记录一次工作中遇到的问题:在泛型中正确使用通配符

前言

前段时间刚看完java编程思想,然后在撸代码的过程中,因为前期做了很好的抽象,所以想使用泛型来简化代码。但是过程中遇到一个问题让我思考了很长时间。现在记录一下。

描述

废话不多说,先贴上代码:

class Father{	
}
class Son extends Father{	
}
public class GerericTest {
	public static void main(String[] args) {		
		Son a=new Son();
		Son b=new Son();
		List<Son> sons=new ArrayList<Son>();
		sons.add(a);
		sons.add(b);
		Map<String, Son> map=new HashMap<String, Son>();
		map.put("a",a);
		map.put("b",b);
		funMethod(sons,map);
	}
	private static void funMethod(List<? extends Father> sons, Map<String, ? extends Father> map) {
		if (sons.size()>0) {
			map.put("c", sons.get(0));
		}
	}
}

这段代码其实没有什么业务含义,纯粹是为了暴漏问题。编译器拒绝编译,报错:

The method put(String, capture#2-of ? extends Father) in the type Map<String,capture#2-of ? extends Father> is not applicable for the arguments (String, capture#3-of ? extends Father)

我来简陋的解释一下:在Map<String,capture#2-of ? extends father>的方法 put (String,capture#2-of ? extends father)和与参数(String,capture#3-of?extends father) 不匹配。

分析

编译器报错:类型不匹配。表面上看map的value的类型 和 sons集合中元素的类型是一样:? extends Father,即父类都是Father的类型。但是编译器的报错信息里 ?号之前还有一个capture的东东,capture后面跟的东西不一样。难道类型的确是不一样的吗?答案是:对,不一样。

通配符 ? extends Father 代表的意思是 任何父类是Father的类型,但是无法确定具体是哪个类。如果定义这样的泛型参数,你只能做查看操作,不能做修改操作:当操作的对象类型不确定时,编译器拒绝做 增删改 操作,这样的操作是不安全的。

解决办法 

那就归回原始,使用明确的类型参数T来代表确切的类型。即:

class Father{
}
class Son extends Father{
}
public class GerericTest {
	public static void main(String[] args) {
		Son a=new Son();
		Son b=new Son();
		List<Son> sons=new ArrayList<Son>();
		sons.add(a);
		sons.add(b);
		Map<String, Son> map=new HashMap<String, Son>();
		map.put("a",a);
		map.put("b",b);
		funMethod(sons,map);
	}
	private static <T extends Father>void funMethod(List<T> sons, Map<String,T> map) {
		if (sons.size()>0) {
			map.put("c", sons.get(0));
		}
	}
}

完美解决问题。这样在方法FunMethond中就可以确定T代表的是父类是Father的一个确定类型,虽然编译期间不知道,但是运行期间会确定这个类型。

问题延申

java编程思想 深入理解jvm虚拟机 中都说过java泛型是编译器通过欺骗实现的。即jvm并不支持这样的语法,但是编译器支持这样的语法,并在编译的时候将类型擦除。为了验证,我尝试着将上面的代码在编译器编译成字节码后,进行反编译,结果如下:

public class GerericTest
{
  public static void main(String[] args)
  {
    Son a = new Son();
    Son b = new Son();
    List sons = new ArrayList();
    sons.add(a);
    sons.add(b);
    Map map = new HashMap();
    map.put("a", a);
    map.put("b", b);
    funMethod(sons, map); }

  private static <T extends Father> void funMethod(List<T> sons, Map<String, T> map) {
    if (sons.size() > 0)
      map.put("c", (Father)sons.get(0));
  }
}

大家看到虽然在funMethod中的泛型定义方式是没有变化的,

但是  map.put("c", sons.get(0))  这行代码经过编译器编译后,通过强制类型转换将类型擦除掉,变成: map.put("c", (Father)sons.get(0)) 

后记

泛型编程是java中的比较高级的概念,虽然不是像c++原生支持,但是依然给编程人员带来很大的方便:编写更通用的代码。以后继续夯实基础,一步一个脚印走下去。

转载于:https://my.oschina.net/huaxiaoqiang/blog/2994191

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值