筐子的历史——java泛型机制学习笔记(一)

来源:互联网 发布:类似于硕鼠的软件 编辑:程序博客网 时间:2024/05/01 11:45

一、泛型出现之前的世界

  自盘古开天辟地至泛型出现之前,世界上只有一种筐子,这种筐子可以存放任意类型的物品,这是好的。比如,苹果,可以遥控的驴,笔记本电脑,航空母舰等等都可以放进这筐子里(这筐子可真够牛X的)。

  像下面这样:

           

  但是这个当中有点儿问题:这个筐子它记不住它所存放东西的类型。比如说,你把上述四种物品都放进去了,封起来存放了几万年,几万年以后你的后代打开它,或者你亲自把它打开(你活的可真够长的),然后取出里面的东西。但是你只知道从筐子里取出的物品的是一个物品,却不知道这物品具体是什么类型,只能把它当做单纯的物品使用。但是如果你还能记得放进去的东西的顺序,还是可以把东西顺利取出来的。

比如:

           

所以,人们以后学聪明了,把同类的东西放在一个筐子里。但是,这筐子仍然是记不住它所存放的类型信息,你虽然希望往可以放苹果里的筐子里存放苹果,但你阻止不了别人往里放驴,当有人从放苹果里的筐子里取出物品时,会肯定的认为他取出的是苹果,即使他实际取出的是只驴。这样会引发混乱。

   比如:

            

世界的制造者是指定不允许出现混乱的,所以泛型腾空出世。


二、c++的泛型机制——半成品筐子

  后来c++出现了,于是世界也发生了翻天覆地的变化。现在世界出现了半成品筐子,用这种筐子可以制作出只能盛放某种物品的成品筐子。

比如,你想盛放几千个苹果,那么可以拿一个半成品筐子制作出来一个只能称苹果的成品筐子,往这个筐子里存放驴的话就存不进去了。

代码:

 

你应该理解清楚上面这段代码都做了什么。首先,声明了一个模版类(即半成品筐子)Basket,执行Basket<Apple> appleBasket(app);时,编译器首先会自动生成一个专门的Basket<Apple>,也就是会产生一段类似于下面这样的代码:

 

上面这段代码实际上相当于一条生产线,把半成品筐子加工成盛放苹果的成品筐子,这条生产线是编译器自动生成的。那么问题来了,如果你想用盛放驴的筐子,那么得产生一个生产盛驴筐子的生产线,即Basket<Donkey>,如果我还想盛放其他物品,那么每种物品都得有这么一天生产其筐子的生产线,也就是c++ 编译器会产生很多类似于上面这段的代码,编译产生的目标文件可能比你想象的要大,这叫做代码膨胀。虽然c++中有代码膨胀,但c++模版类的内部是可以得到类型信息的,由于java中有类型擦出,所以java泛型类内部的类型信息就很受限,这是后话。(关于c++的模版及其深层机制我也不是十分了解,各位可以自己去google下,推荐关键词:c++模版,c++代码膨胀,模版+代码膨胀)

三、java的泛型机制

  java中的类型机制貌似很聪明。在java中,盛物品的筐子仍然是盛物品的筐子,不过这筐子上多了个可以贴标签的地方。比如我想要一个盛放苹果的筐子,那么我就拿一个盛物品的筐子,上面贴上“苹果”标签,那么你往这筐子里放的时候就会注意到放驴进去是不对的,所以基本可以保证这筐子里盛的都是苹果。以后从这筐子里取出物品的时候直接当成苹果用就可以了,不必经过专家辨认了。

看代码:

 

与c++世界不同的是,java世界中的筐子本质上都是可以盛放任意物品的,只不过有的筐子上贴上了标签一说明这个筐子只用于存放某一种特定物品。实际上也存在没有标签的筐子。java中不会为每个筐子都自动产生一个生产线,所以也不存在代码膨胀的问题。这也是c++与java对泛型的实现机制的本质不同造成的。(这也是后话)

四、我不是世界创造者

   我不是世界的创造者,所以我管理不了这混乱,你要是也混乱了,请找世界创造者解决。不过如果我的文章出现混乱了,您可得帮我解决,对我来说,您比世界创造者还重要。