Java编程思想之范型(2)

本文为Java编程思想之范型第二篇,第一篇通过一些问题介绍了Java范型引人的目的,范型相关的类型察除(erasure)、范型边界(boundary)、通配符(wildcard,问号)等,本篇继续从Java范型的使用范围,实际使用过程中常见的坑以及与C++、Python等语言的对比扩展对Java范型的理解和认识。

从计算机编程语言的角度来讲,范型让代码更加通用和简洁:

编写更通用的代码。利用范型可以提高代码的抽象性,越是抽象的代码,通用性越高。例如:在S种集合类型(如:List、Map、Set)的数据结构上实现A种算法(如:sort,top,sum),每一种数据结构上要实现每一种算法,因此需要S*A种实现;而引入范型后,通过Iterator,每一种算法操作的是Iterator,Iterator抽象出了不同集合类型的共性,所以只需要S+A种实现。

编写更简洁的代码。

我们先看下C++的范型例子。

图一:C++范型样例一

C++的范型(模版,templates)在编译期进行类型检查,因此是类型安全的;C++的模版包括函数模版和类模版。图一中,maxAlgo为模版方法,支持原始类型参数。在编译期,编译器检查所有List的调用,模版参数类型是否包含getId()方法。如果有则编译正常,否则如图二所示,编译器检查类型参数Other类没有getId方法,提示no member name `getId` in ‘Other’错误。

图二:C++范型样例二

像C++在编译器进行类型检查的语言被称为静态语言。相对的,Python是一种动态类型语言,即在运行期进行类型检查。

图三:Python范型样例

C++和Python的这种行为被称为潜在的类型机制(latent typing,请大家记住这个术语)。这种机制只要求范型参数拥有相应的参数签名,并不关心它们是什么类型的或实现了什么接口,因此也是彻底的“泛化”。而Java的范型并非彻底的泛化,因为它把范型参数察除为Object类型或者限定为特定的超类(或接口)。如图四所示,Caller的m方法有一个范型参数T t,要在m方法中调用printVersion方法就必须保证范型参数类型T继承或实现一个带有printVersion签名的类型或接口。

图四:Java范型样例

除了latent typing,Java范型以下几点也需要大家在实战中注意:

基本类型不能作为Java范型的类型参数;不过Java提供了自动装包机制,基本类型会自动包装为对象类型,但数组除外,参见图五。

图五:Java的自动装包样例

基类会劫持范型接口。例如图六:基类Basic实现了以Basic作为类型参数的Comparable接口,而子类Chd1不允许实现带有Chd1的comparable接口,必须和基类保持一致。

图六:Java范型的基类劫持

不能直接创建范型对象和数组。而在C++中都是允许的,例如图一中的范例。在Java中可以通过通过传递Class参数,利用class创建实例和数组。

图七:Java范型的对象创建

总之,Java范型是在Java1.5版本引入,为了兼容老版本做了一些妥协,与C++等灵活强大的范型相比,Java的范型显得不是那么彻底,但开发者还是可以通过诸如反射、设计模式来弥补这些不足。

用Bruce Eckel在《Java编程思想》范型这一章的一句话结束本篇:

“…by understanding the boundaries, you become amore powerful programmer. By knowing what you can’t do, you can make better useof what you can do (partly because you don’t waste time bumping up againstwalls).” 码头军认为:知边界、知非,为深知。

  • 发表于:
  • 原文链接https://kuaibao.qq.com/s/20180815G0VQKK00?refer=cp_1026
  • 腾讯「云+社区」是腾讯内容开放平台帐号(企鹅号)传播渠道之一,根据《腾讯内容开放平台服务协议》转载发布内容。

同媒体快讯

扫码关注云+社区

领取腾讯云代金券

年度创作总结 领取年终奖励