PMD 将举报以下违规行为:
ArrayList<Object> list = new ArrayList<Object>();
违规行为是“避免使用‘ArrayList’等实现类型;而是使用接口”。
以下行将纠正违规行为:
List<Object> list = new ArrayList<Object>();
为什么要用后者加
List
而不是ArrayList
?
在具体类型上使用接口是良好封装和松散耦合代码的关键。
在编写自己的 API 时遵循这种做法甚至是一个好主意。如果这样做,您稍后会发现向代码添加单元测试(使用模拟技术)以及在将来需要时更改底层实现会更容易。
这是一篇关于该主题的好文章。
希望有帮助!
这是首选方法,因为您可以将代码与列表的实现分离。使用该接口,您可以轻松地将实现(本例中为 ArrayList)更改为另一个列表实现,而无需更改任何其余代码,只要它仅使用 List 中定义的方法即可。
总的来说,我同意将接口与实现解耦是一件好事,并且将使您的代码更易于维护。
但是,您必须考虑一些例外情况。通过接口访问对象会增加一个额外的间接层,这会使您的代码变慢。
出于兴趣,我进行了一项实验,对 100 万长度的 ArrayList 生成了 100 亿次顺序访问。在我的 2.4Ghz MacBook 上,通过 List 接口访问 ArrayList 平均需要 2.10 秒,而声明 ArrayList 类型时平均需要 1.67 秒。
如果您正在处理大型列表、内部循环深处或经常调用的函数,那么这是需要考虑的事情。
ArrayList 和 LinkedList 是 List 的两种实现,List 是有序的项目集合。从逻辑上讲,使用 ArrayList 还是 LinkedList 并不重要,因此您不应该将类型限制为那样。
这与 Collection 和 List 形成对比,它们是不同的东西(List 意味着排序,Collection 则不然)。
为什么要使用后者配合List而不是ArrayList?
这是一个很好的实践:针对接口进行编程而不是实现
通过将
ArrayList
替换为 List
,您可以根据您的业务用例在将来更改 List
实现,如下所示。
List<Object> list = new LinkedList<Object>();
/* Doubly-linked list implementation of the List and Deque interfaces.
Implements all optional list operations, and permits all elements (including null).*/
或
List<Object> list = new CopyOnWriteArrayList<Object>();
/* A thread-safe variant of ArrayList in which all mutative operations
(add, set, and so on) are implemented by making a fresh copy of the underlying array.*/
或
List<Object> list = new Stack<Object>();
/* The Stack class represents a last-in-first-out (LIFO) stack of objects.*/
或
其他一些
List
具体实施。
List
接口定义了合约,List
的具体实现是可以改变的。这样,接口和实现是松散耦合的。
相关SE问题:
即使对于局部变量,在具体类上使用接口也会有所帮助。您最终可能会调用接口外部的方法,然后在必要时很难更改 List 的实现。 此外,最好在声明中使用最不具体的类或接口。如果元素顺序不重要,请使用集合而不是列表。这为您的代码提供了最大的灵活性。
您的类/接口的属性应该通过接口公开,因为它为您的类提供了要使用的行为契约,而不管实现如何。
但是...
在局部变量声明中,这样做没有什么意义:
public void someMethod() {
List theList = new ArrayList();
//do stuff with the list
}
如果是局部变量,则使用类型即可。它仍然可以隐式向上转换为其适当的接口,并且您的方法应该希望接受其参数的接口类型,但对于局部变量,使用实现类型作为容器是完全有意义的,以防万一您确实需要实现 -具体功能。
一般来说,对于您的代码行来说,打扰接口是没有意义的。但是,如果我们谈论 API,就有一个非常好的理由。我有小班课
class Counter {
static int sizeOf(List<?> items) {
return items.size();
}
}
在这种情况下需要使用接口。因为我想计算每种可能的实现的大小,包括我自己的自定义。
class MyList extends AbstractList<String>...
。
接口暴露给最终用户。一个类可以实现多个接口。暴露于特定接口的用户可以访问该特定接口中定义的某些特定行为。
一个接口也有多个实现。基于场景的系统将适用于不同的场景(接口的实现)。
如果您需要更多解释,请告诉我。
接口在调试器视图中通常比具体类具有更好的表示形式。