如何让Java接口具有HAS-A关系
Java接口如何拥有HAS-A关系的问题出现的原因是因为关于接口关系的陈述是过于简化的。实际上,HAS-A关系是在实现类之间建立的。如果接口设计得以一种方式,使得所有的实现类都始终建立一个HAS-A关系,那么我们可以说接口之间存在HAS-A关系。
对于Iterator接口来说,并不总是存在HAS-A关系。一个接口可以通过声明至少一个依赖于其他对象的方法(例如一个访问器方法)来建立一个始终存在的HAS-A关系。然而,它仍然无法强制实现类以预期的方式实现它。例如,一个实现类可以通过始终抛出异常来实现这个方法。但是,如果接口的契约暗示存在HAS-A关系,我们可以忽略违反契约的实现。
对于Iterator接口来说,它没有访问器方法,但是有一个被称为remove()的方法,它被假定会影响源集合。但是这个方法被标记为可选操作,并且明确允许抛出UnsupportedOperationException异常。因此,虽然迭代器实现通常与它所创建的集合具有HAS-A关系,但并非总是如此。
一个反例是CopyOnWriteArrayList。当你在它上面调用iterator()方法时,返回的迭代器将与底层数组具有HAS-A关系。因此,在迭代器创建时,集合和迭代器都与该数组具有HAS-A关系,但是当你修改集合时,它将与一个新的数组建立HAS-A关系,所以在修改后,迭代器和集合完全解耦。
此外,并不是每个迭代器都源自于一个集合。例如:
Iterator
Iterator
请参阅Collections.emptyIterator()和BaseStream.iterator()。
注意,对于某些情况,尤其是不可变集合,我们可以说迭代器在概念上与集合具有HAS-A关系,因为它们提供对其内容的访问,即使以不同的方式实现。这就是为什么上面的例子被谨慎选择来反驳该陈述的概念层面,要么是因为没有集合,要么是因为集合在修改时明确地与迭代器解耦。
因此,尽管接口之间不直接建立HAS-A关系,但在某些情况下,可以通过接口的设计和实现来实现这种关系。