教程中国
PHOTOSHOP CS9.0中文版 MAYA 8.5 FOR WINDOWS Corel Painter v9.0 Flash MX2004 中文版 Illustrator cs2 中文版
VC++6.0含sp6 中英文版 VB6.0 +sp6 简体中文版 Borland Delphi 7汉化版 MSDN for vb6.0中文版 Visual Studio 2005简体
教程中国下属 文件存储共享专家BIBIDU.COM 提供大型软件,教材,源码,电影,音乐,图书等下载 更多精品请点此进入
  您目前所在位置: 教程中国 >> 编程基地 >> JAVA >> Java 理论与实践: 伪typedef反模式 RSS订阅
Java 理论与实践: 伪typedef反模式
Java 理论与实践: 伪typedef反模式(1)
教程(视频,书籍)下载:  ASP.NET AutoCAD 数据库 C# ASP java photoshop 网页设计 delphi 3dmax Flash C++ VB 张孝祥 实例   更多请进入BIBIDU搜索
IT搜索引擎   

将泛型添加到 Java语言中增加了类型系统的复杂性,提高了许多变量和方法声明的冗长程度。因为没有提供 “typedef” 工具来定义类型的简短名称,所以有些开发人员转而把扩展当作 “穷人的 typedef”,结果收到了良好的效果。

对于 Java 5.0 中新增的泛型工具,一个常见的抱怨就是,它使代码变得太冗长。原来用一行就够的变量声明不再存在了,与声明参数化类型有关的重复非常讨厌,特别是还没有良好地支持自动补足的 IDE。例如,如果想声明一个 Map,它的键是 Socket,值是 Future,那么老方法就是:

Map socketOwner = new HashMap();

比新方法紧凑得多:

Map>socketOwner = new HashMap>();

当然,新方法内置了更多类型信息,减少了编程错误,提高了程序的可读性,但是确实带来了更多声明变量和方法签名方面的前期工作。类型参数在声明和初始化中的重复看起来尤其没有必要;Socket 和 Future需要输入两次,这迫使我们违犯了 “DRY” 原则(不要重复自己)。

合成类似于 typedef 的东西

添加泛型给类型系统增加了一些复杂性。在 Java 5.0 之前,“type” 和 “class” 几乎是同义的,而参数化类型,特别是那些绑定的通配类型,使子类型和子类的概念有了显著区别。类型 ArrayList、ArrayList和 ArrayList是不同的类型,虽然它们是由同一个类 ArrayList 实现的。这些类型构成了一个层次结构;ArrayList是 ArrayList的超类型,而 ArrayList是 ArrayList的超类型。

对于原来的简单类型系统,像 C 的 typedef 这样的特性没有意义。但是对于更复杂的类型系统,typedef 工具可能会提供一些好处。不知是好还是坏,总之在泛型加入的时候,typedef 没有加入 Java 语言。

有些人用作 “穷人的 typedef” 的一个(坏的)做法是一个小小的扩展:创建一个类,扩展泛型类型,但是不添加功能,例如 SocketUserMap 类型,如清单 1 所示:

清单 1. 伪 typedef 反模式 —— 不要这么做

public class SocketUserMap extends HashMap>{ } SocketUserMap socketOwner = new SocketUserMap();

我将这个技巧称为伪 typedef 反模式,它实现了将 socketOwner 定义简化为一行的这一(有问题的)目标,但是有些副作用,最终成为重用和维护的障碍。(对于有明确的构造函数而不是无参构造函数的类来说,派生类也需要声明每个构造函数,因为构造函数没有被继承。)

伪类型的问题

在 C 中,用 typedef 定义一个新类型更像是宏,而不是类型声明。定义等价类型的 typedef,可以与原始类型自由地互换。清单 2 显示了一个定义回调函数的示例,其中在签名中使用了一个 typedef,但是调用者提供给回调的是一个等价类型,而编译器和运行时都可以接受它:

清单 2. C 语言的 typedef 示例

// Define a type called "callback" that is a function pointer typedef void (*Callback)(int); void doSomething(Callback callback) { } // This function conforms to the type defined by Callback void callbackFunction(int arg) { } // So a caller can pass the address of callbackFunction to doSomething void useCallback() { doSomething(&callbackFunction); }

扩展不是类型定义

用 Java 语言编写的试图使用伪 typedef 的等价程序就会出现麻烦。清单 3 的 StringList 和 UserList 类型都扩展了一个公共超类,但是它们不是等价的类型。这意味着任何想调用 lookupAll 的代码都必须传递一个 StringList,而不能是 List或 UserList。

清单 3. 伪类型如何把客户限定在只能使用伪类型

class StringList extends ArrayList{ } class UserList extends ArrayList{ } ... class SomeClass { public void validateUsers(UserList users) { ... } public UserList lookupAll(StringList names) { ... } }

这个限制要比初看上去严格得多。在小程序中,可能不会有太大差异,但是当程序变大的时候,使用伪类型的需求就会不断地造成问题。如果变量类型是 StringList,就不能给它分配普通的 List,因为 List是 StringList 的超类型,所以不是 StringList。就像不能把 Object 分配给类型为 String 的变量一样,也不能把 List分配给类型为 StringList 的变量(但是,可以反过来,例如,可以把 StringList 分配给类型为 List的变量,因为 List是 StringList 的超类型。)

同样的情况也适用于方法的参数;如果一个方法参数是 StringList 类型,那么就不能把普通的 List传递给它。这意味着,如果不要求这个方法的每次使用都使用伪类型,那么根本不能用伪类型作为方法参数,而这在实践当中就意味着在库 API 中根本就不能使用伪类型。而且大多数库 API 都源自本来没想成为库代码的那些代码,所以 “这个代码只是给我自己的,没有其他人会用它” 可不是个好借口(只要您的代码有一点儿用处,别人就有可能会使用它;如果您的代码臭得很,那您可能是对的)。

伪类型会传染

这种 “病毒” 性质是让 C 代码的重用有困难的因素之一。差不多每个 C 包都有头文件,定义工具宏和类型,像 int32、boolean、true、false,诸如此类。如果想在一个应用程序内使用几个包,而它们对于这些公共条目没有使用相同的定义,那么即使要编译一个只包含所有头文件的空程序,之前也要在 “头文件地狱” 问题上花好长时间。如果编写的 C 应用程序要使用许多来自不同作者的不同的包,那么几乎肯定要涉及一些这类痛苦。另一方面,对于 Java应用程序来说,在没有这类痛苦的情况下使用许多甚至更多的包,是非常常见的事。如果包要在它们的 API 中使用伪类型,那么我们可能就要重新经历早已留在痛苦回忆中的问题。

作为示例,假设有两个不同的包,每个包都用伪类型反模式定义了 StringList,如清单 4 所示,而且每个包都定义了操作 StringList 的工具方法。两个包都定义了同样的标识符,这一事实已经是不方便的一个小源头了;客户程序必须选择导入一个定义,而另一个定义则要使用完全限定的名称。但是更大的问题是现在这些包的客户无法创建既能传递给 sortList 又能传递给 reverseList 的对象,因为两个不同的 StringList 类型是不同的类型,彼此互不兼容。客户现在必须在使用一个包还是使用另一个包之间进行选择,否则他们就必须做许多工作,在不同类型的 StringList 之间进行转换。对包的作者来说以为方便的东西,成为在所有地方使用这个包的突出障碍,除非在最受限的环境中。

清单 4. 伪类型的使用如何妨碍重用

package a; class StringList extends ArrayList{ } class ListUtilities { public static void sortList(StringList list) { } } package b; class StringList extends ArrayList{ } class SomeOtherUtilityClass { public static void reverseList(StringList list) { } } ... class Client { public void someMethod() { StringList list = ...; // Can't do this ListUtilities.sortList(list); SomeOtherUtilityClass.reverseList(list); } }

伪类型通常太具体

伪类型反模式的进一步问题是,它会丧失使用接口定义变量类型和方法参数的好处。虽然可以把 StringList 定义成扩展 List的接口,再定义一个具体类型 StringArrayList 来扩展 ArrayList并实现 StringList,但多数伪 typedef 反模式的用户通常达不到这种水平,因为这项技术的目的主要是为了简化和缩短类型的名称。但结果是,API 的用处减少了并变得更脆弱,因为它们使用 ArrayList 这样的具体类型,而不是 List 这样的抽象类型。

来源:upschool.com.cn
作者:
关键字:Java,理论,实践
发表日期:2006-6-12

网页显示有限 阅读全文请下载本文完整版WORD文档

上一篇:看看你能过关吗-J2EE面试题集锦   下一篇:你真的掌握J2SE了么-J2SE学习标准篇


共2页 9 7 [1] [28 :>

2009-1-8 5:06:46
本文的相类似文章
  • Java 中对文件的读写操作之比较
  • 网页界面设计教程一:基础理论
  • javascript读取RSS数据
  • ANT 操控 ORACLE数据库实践
  • 在Oracle中发布和调用Java functions
  • Oracle PL/SQL入门之案例实践
  • java连接oracle,取io文件内容,子串替换
  • 用JAVA实现ORACLE的文件上传,下载
  • oracle数据库java接口类
  • 解析 KJava 在移动设备开发支持中的应用
  • 在学习中进步 在进步中成长 教程中国相随您的成长之路
    华腾联合科技股份有限公司版权所有
    广告联系:Rosibo@163.com