64. 通过接口引用对象

      而不是这样:

      如果你养成了使用接口作为类型的习惯,那么你的程序将更加灵活。 如果你决定要切换实现,只需在构造函数中更改类名(或使用不同的静态工厂)。例如,第一个声明可以改为:

      有一点值得注意:如果原实现提供了接口的通用约定不需要的一些特殊功能,并且代码依赖于该功能,那么新实现提供相同的功能就非常重要。例如,如果围绕第一个声明的代码依赖于 的排序策略,那么在声明中将 HashSet 替换为 LinkedHashSet 将是不正确的,因为 HashSet 不保证迭代顺序。

      那么,为什么要更改实现类型呢?因为第二个实现比原来的实现提供了更好的性能,或者因为它提供了原来的实现所缺乏的理想功能。例如,假设一个字段包含一个 HashMap 实例。将其更改为 将为迭代提供更好的性能和与键的自然顺序,但是你只能在键类型为 enum 类型的情况下使用 EnumMap。将 HashMap 更改为 LinkedHashMap 将提供可预测的迭代顺序,性能与 HashMap 相当,而不需要对键类型作出任何特殊要求。

      如果没有合适的接口存在,那么用类引用对象是完全合适的。 例如,考虑值类,如 和 BigInteger。值类很少在编写时考虑到多个实现。它们通常是 final 的,很少有相应的接口。使用这样的值类作为参数、变量、字段或返回类型非常合适。

      没有合适接口类型的第二种情况是属于框架的对象,框架的基本类型是类而不是接口。如果一个对象属于这样一个基于类的框架,那么最好使用相关的基类来引用它,这通常是抽象的,而不是使用它的实现类。在 java.io 类中许多诸如 OutputStream 之类的就属于这种情况。

      这三种情况并不是面面俱到的,而仅仅是为了传达适合通过类引用对象的情况。在实际应用中,给定对象是否具有适当的接口应该是显而易见的。如果是这样,如果使用接口引用对象,程序将更加灵活和流行。如果没有合适的接口,就使用类层次结构中提供所需功能的最底层的类