为什么应该首选 Java 类的接口?

问题描述 投票:0回答:10

PMD 将举报以下违规行为:

ArrayList<Object> list = new ArrayList<Object>();

违规行为是“避免使用‘ArrayList’等实现类型;而是使用接口”。

以下行将纠正违规行为:

List<Object> list = new ArrayList<Object>();

为什么要用后者加

List
而不是
ArrayList

java collections interface
10个回答
84
投票

在具体类型上使用接口是良好封装和松散耦合代码的关键。

在编写自己的 API 时遵循这种做法甚至是一个好主意。如果这样做,您稍后会发现向代码添加单元测试(使用模拟技术)以及在将来需要时更改底层实现会更容易。

这是一篇关于该主题的好文章

希望有帮助!


33
投票

这是首选方法,因为您可以将代码与列表的实现分离。使用该接口,您可以轻松地将实现(本例中为 ArrayList)更改为另一个列表实现,而无需更改任何其余代码,只要它仅使用 List 中定义的方法即可。


12
投票

总的来说,我同意将接口与实现解耦是一件好事,并且将使您的代码更易于维护。

但是,您必须考虑一些例外情况。通过接口访问对象会增加一个额外的间接层,这会使您的代码变慢。

出于兴趣,我进行了一项实验,对 100 万长度的 ArrayList 生成了 100 亿次顺序访问。在我的 2.4Ghz MacBook 上,通过 List 接口访问 ArrayList 平均需要 2.10 秒,而声明 ArrayList 类型时平均需要 1.67 秒。

如果您正在处理大型列表、内部循环深处或经常调用的函数,那么这是需要考虑的事情。


6
投票

ArrayList 和 LinkedList 是 List 的两种实现,List 是有序的项目集合。从逻辑上讲,使用 ArrayList 还是 LinkedList 并不重要,因此您不应该将类型限制为那样。

这与 Collection 和 List 形成对比,它们是不同的东西(List 意味着排序,Collection 则不然)。


2
投票

为什么要使用后者配合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问题:

“编程到接口”是什么意思?


1
投票

即使对于局部变量,在具体类上使用接口也会有所帮助。您最终可能会调用接口外部的方法,然后在必要时很难更改 List 的实现。 此外,最好在声明中使用最不具体的类或接口。如果元素顺序不重要,请使用集合而不是列表。这为您的代码提供了最大的灵活性。


1
投票

您的类/接口的属性应该通过接口公开,因为它为您的类提供了要使用的行为契约,而不管实现如何。

但是...

在局部变量声明中,这样做没有什么意义:

public void someMethod() {
List theList = new ArrayList();
//do stuff with the list
}

如果是局部变量,则使用类型即可。它仍然可以隐式向上转换为其适当的接口,并且您的方法应该希望接受其参数的接口类型,但对于局部变量,使用实现类型作为容器是完全有意义的,以防万一您确实需要实现 -具体功能。


1
投票

一般来说,对于您的代码行来说,打扰接口是没有意义的。但是,如果我们谈论 API,就有一个非常好的理由。我有小班课

class Counter {
    static int sizeOf(List<?> items) {
        return items.size();
    }
}

在这种情况下需要使用接口。因为我想计算每种可能的实现的大小,包括我自己的自定义。

class MyList extends AbstractList<String>...


0
投票

接口暴露给最终用户。一个类可以实现多个接口。暴露于特定接口的用户可以访问该特定接口中定义的某些特定行为。

一个接口也有多个实现。基于场景的系统将适用于不同的场景(接口的实现)。

如果您需要更多解释,请告诉我。


0
投票

接口在调试器视图中通常比具体类具有更好的表示形式。

© www.soinside.com 2019 - 2024. All rights reserved.