9.5.6 自己写算法
当你要写一个接收一个集合类对象作为参数的方法的时候,在方法中要尽量操作接口而不是具体实现。例如,假设你想要用一系列menu item去填充一个JMenu。自然而然的想法会是这样:
void fillMenu(JMenu menu, ArrayList<JMenuItem> items){
for(JMenuItem item: items)
menu.add(item);
}
这样的实现对该方法的调用者施加了不必要的约束:调用者必须提供一个ArrayList。如果调用者需要处理另一个容器,就只能把它重新包装成ArrayList才能使用你的方法。让你的方法接收一个更通用的容器就好得多。
你应该问问你自己:能够完成这项操作的最通用的接口是什么?在这个例子中,你只需要能够访问所有元素而已,这是基本的Collection接口就能提供的功能。考虑过这些之后,你应该重写fillMenu方法来接收任何一个集合类:
void fillMenu(JMenu menu, Collection<JMenuItem> items){
for(JMenuItem item: items)
menu.add(item);
}
现在,任何人都能使用ArrayList或者LinkedList来调用该方法。甚至是使用Arrays.asList包装的数组也能作为该方法的参数。
Note
如果使用接口作为方法参数这么好,为什么标准库不总是这么干呢?例如JComboBox有两个构造方法:
JComboBox(Object[] items)
JComboBox(Vector<?> items)
原因在时间上,Swing库创建的时间要早于集合类库。所以一开始只有第一条构造方法,在集合类库加入后,又有了第二条构造方法。
当你编写返回一个集合类对象的方法时,你也希望能返回一个接口而不是类,这样在将来你可以更改这个方法的实现并返回一个不同的类。
例如,我们编写方法getAllItems返回一个menu中的所有item:
List<JMenuItem> getAllItems(JMenu menu){
List<JMenuItem> items = new ArrayList<>();
for(int i = 0; i < menu.getItemCount(); i++)
items.add(menu.getItem(i));
return items;
}
之后,你可能决定不再复制menu中的Item到新的内存,而仅仅提供一个视窗来查看这些Item。这可以通过返回一个AbstractList的匿名子类来实现:
List<JMenuItem> getAllItems(final JMenu menu){
return new AbstractList<>(){
public JMenuItem get(int i){return menu.getItem(i);}
public int size(){return menu.getItemCount();}
};
}
你就需要在方法的说明文档中明确指出哪些操作是可行的。在本例中,你必须告知调用者,该方法返回的List对象是不可修改的。