在《算法》中的散列表一节,在用拉链法实现散列表的API时要求实现以下一个方法:
public Iterable<Key> keys()
我们知道Iterable是一个接口,那么一个方法怎么会返回一个接口呢?在《Effective Java》中第52条为“通过接口引用对象”
as parameter types. More generally, you should favor the use of interfaces rather than classes to refer to objects. If appropriate interface types exist, then parameters, return values, variables, and fields should all be declared using interface types. The only time you really need to refer to an object’s class is when you’re creating it with a constructor. To make this concrete, consider the case of Vector, which is an implementation of the List interface. Get in the habit of typing this: // Good - uses interface as type
List<Subscriber> subscribers = new Vector<Subscriber>(); rather than this: // Bad - uses class as type!
Vector<Subscriber> subscribers = new Vector<Subscriber>(); If you get into the habit of using interfaces as types, your program will be much more flexible. If you decide that you want to switch implementations, all you have to do is change the class name in the constructor (or use a different static factory). For example, the first declaration could be changed to read List<Subscriber> subscribers = new ArrayList<Subscriber>(); and all of the surrounding code would continue to work. The surrounding code was unaware of the old implementation type, so it would be oblivious to the change. There is one caveat: if the original implementation offered some special functionality not required by the general contract of the interface and the code depended on that functionality, then it is critical that the new implementation provide the same functionality. It is entirely appropriate to refer to an object by a class rather than an interface if no appropriate interface exists. For example, consider value classes, such as String and BigInteger. Value classes are rarely written with multiple implementations in mind. They are often final and rarely have corresponding interfaces. It is perfectly appropriate to use such a value class as a
parameter, variable, field, or return type. More generally, if a concrete class has no associated interface, then you have no choice but to refer to it by its class whether or not it represents a value. The Random class falls into this category. A second case in which there is no appropriate interface type is that of objects belonging to a framework whose fundamental types are classes rather than interfaces. If an object belongs to such a class-based framework, it is preferable to refer to it by the relevant base class, which is typically abstract, rather than by its implementation class. The java.util.TimerTask class falls into this category. A final case in which there is no appropriate interface type is that of classes that implement an interface but provide extra methods not found in the interface— for example, LinkedHashMap. Such a class should be used to refer to its instances only if the program relies on the extra methods. It should rarely be used as a parameter type (Item 40). These cases are not meant to be exhaustive but merely to convey the flavor of situations where it is appropriate to refer to an object by its class. In practice, it should be apparent whether a given object has an appropriate interface. If it does, your program will be more flexible if you use the interface to refer to the object; if not, just use the least specific class in the class hierarchy that provides the required functionality. Reference: Effective Java 2nd Edition by Joshua Bloch
子类是可以向上转型为父类,因此传递给父类的消息子类是都可以接受的,接口和其实现类有点这样的关系,当然还是有不同。当我们返回一个接口类型时,其实就是意味着,以后你完全可以把其返回值赋给一个实现了此接口的实现类的实例。这样编写的好处是,此接口的实现类的实例在运行时可以任意灵活指定,而不需要修改接口函数的代码。如果有合适的接口类型存在,那么对于参数,返回值,变量和域来说,都应该使用接口类型进行声明。如果没有合适的接口存在,完全可以用类而不是接口来引用对象。
查看JDK的Collections相关的库可以发现大量使用了返回接口来引用对象,例如iterator()这些方法的实现都是返回一个interface,实际只需要实现这个interface即可,对于不同的集合实现interator不一样并不影响iterator的调用。
回到最开始public Iterable<Key> keys()该如何实现,这里采用ArrayList来实现,当然也可以返回其他任何实现了Iterable接口的类:
public Iterable<Key> keys() {
ArrayList<Key> ka = new ArrayList<Key>();
for (int i = 0; i < M; i++) {
ka.addAll(st[i].keys());
}
return ka;
}