人们常常看到变量应该用某个接口声明的建议,而不是实现类。例如:
List<Integer> list = new ArrayList<>();
然而,假设我使用此列表的算法确实依赖于ArrayList
的O(1)随机访问(例如Fisher-Yates shuffling)。在这种情况下,ArrayList
代表我的关键抽象是它的数组性质,而不仅仅是它的List性质。换句话说,如果有人出现并将list
更改为LinkedList
,即使代码可以编译,也会出现问题。在这种情况下,声明是否可以使用实现类型?,例如:
ArrayList<Integer> list = new ArrayList<>();
答案 0 :(得分:3)
要强调Oliver Charlesworth在评论中所说的话:这些信息是否公开是最重要的区别。
从编写问题的方式来看,您似乎在谈论字段(而不是方法参数或返回值)。根据定义,私有字段是一个实现细节,您可以使其具有任意特定性。因此,将其声明为ArrayList
是可行的 - 尽管严格来说,这是不相关的。用这句话来说:你说
如果有人......将
list
更改为LinkedList
,则会出现问题,...
可以从
更改声明的人private List<T> list = new ArrayList<T>();
到
private List<T> list = new LinkedList<T>();
也可以从
更改声明private ArrayList<T> list = new ArrayList<T>();
到
private LinkedList<T> list = new LinkedList<T>();
防止这种情况的唯一可行方法是将此(重要)实现细节添加为注释,例如
/**
* The list that stores ... whatever.
*
* This list should have a complexity of O(1) for random access,
* because ...
*/
private final List<T> list = new ArrayList<T>();
(这也可以通过将列表内部传递给期望它为RandomAccess
的方法来强制执行。这是由AlexR提出的,但引用了public
方法。对于{{1}方法,如果没有记录意图和原因需要private
,那么即使这样也不会阻止某人更改方法签名。
答案 1 :(得分:2)
我认为使用接口仍然非常重要。你的情况非常特殊但是有一种方法可以在不使用具体类的情况下解决它。有一个名为RandomAccess
的界面。标记接口不会声明方法,但应由实现者或List
使用,它们提供对元素的randome访问。
因此,您可以定义需要随机访问列表的方法,如下所示:
public <E, L extends List<E> & RandomAcess>void algorithm(L list);
您将无法将链接列表传递给此方法,但ArrayList
将起作用。正如您所期望的那样,您可以在不更改算法的情况下更改列表的实现。例如,您可以使用线程安全CopyOnWriteArrayList
,这将允许您在没有显式syncrhonized
语句的情况下处理多线程。