一般来讲,应该优先使用接口而不是类来引用对象。如果有合适的接口类型,那么对于参数、返回值、变量和域来说,都应该使用接口类型来进行声明。如果你养成了使用接口作为类型的习惯,你的程序将会更加灵活。
不推荐写法:
Vector subsribers = new Vector();
推荐写法:
List subsribers = new Vector();
List是Vector实现的接口,这里使用了List接口作为引用;这样当你决定更好实现时,比如说,我不需要Vector这种做了并发处理的工具类,想改用ArrayList,那么要做到的知识改变构造器中累的名称。
List subsribers = new ArrayList();
这样子,周围的所有代码的都可以继续工作,且不知道你已经将原来的实现类型变更了,对于这个变化是无感知的。
上面提到,将实现修改而周围代码无感知。
有一点值得注意:如果原来的实现提高了某种特殊的功能,而这种功能并不是抽象接口所提供的通用约定,并且周围代码又依赖这个功能。那么,很关键的是,新的实现也必须提供相同的功能才可以保证,修改实现的代码运行如初。
以上面为例,我们将实现从Vector 改为 ArrayList,而恰好场景属于高并发的场景,周围业务也依赖于Vector 的同步策略,而ArrayList 是线程不安全的工具类,那么我们得出结论:使用ArrayList 替代Vector 是不正确的。
所以,我们在写代码时,如果业务依赖于实现类的任何特殊属性,最好能够在变量的地方给这个需求建立文档说明。
很简单,因为新的实现类提供了更好的性能,或者因为它提供了期望得到的额外功能。
举个例子:ThreadLocal 类,在它的内部,这个类在Thread 中石油了一个包级私有的Map 域,将每个线程的值(per-thread values)与ThreadLocal 实例关联起来。
在java 1.3 发行版中,这个域初始化为HashMap 实例。
在java 1.4 发行版中,这个域初始化为IdentityHashMap 实例。(IdentityHashMap是专用的Map实现,感兴趣的同学可以自行在jdk中查看源码)
如此一改,将会让ThreadLocal 机制变快许多。
使用接口类型声明域“让程序员保持诚实”,因为凡是错误修改了实现类的地方,最终在编译阶段都会出错,从而减少后续运行时出现风险的代价。
但我们还是要清楚几个不适合使用接口作为引用的场景: