多态性:为什么使用"List list = new ArrayList"而不是"ArrayList list = new ArrayList"?

hqt*_*hqt 139 java polymorphism interface list

可能重复:
为什么要优先选择Java类的接口?

我应该什么时候使用

List<Object> list = new ArrayList<Object>();
Run Code Online (Sandbox Code Playgroud)

ArrayList继承自List,所以如果某些功能ArrayList不在List,那么我将失去一些功能ArrayList,对吧?编译器在尝试访问这些方法时会注意到错误?

Ale*_*ood 244

您这样做的主要原因是将代码与特定的接口实现分离.当你编写这样的代码时:

List list = new ArrayList();  
Run Code Online (Sandbox Code Playgroud)

其余的代码只知道数据是类型的List,这是更好的,因为它允许您List轻松地在接口的不同实现之间切换.

例如,假设您正在编写一个相当大的第三方库,并说您决定使用一个实现库的核心LinkedList.如果您的图书馆在很大程度上依赖于访问这些列表中的元素,那么最终您会发现您做出了糟糕的设计决策; 你会意识到你应该使用a ArrayList(它给出O(1)访问时间)而不是a LinkedList(它给出O(n)访问时间).假设您已经编程到接口,那么进行这样的更改很容易.你只需要更改Listfrom 的实例,

List list = new LinkedList();
Run Code Online (Sandbox Code Playgroud)

List list = new ArrayList();  
Run Code Online (Sandbox Code Playgroud)

并且您知道这将有效,因为您已编写代码以遵循List接口提供的合同.

另一方面,如果您使用了库的核心LinkedList list = new LinkedList(),那么进行这样的更改将不会那么容易,因为无法保证您的其余代码不会使用特定于LinkedList该类的方法.

总而言之,选择只是设计问题......但这种设计非常重要(特别是在处理大型项目时),因为它可以让您在不破坏现有代码的情况下进行特定于实现的更改.

  • 很多人都没有提到通过ArrayList list = new ArrayList()将它实例化为局部变量是完全可以接受的.在方法签名中使用List接口或作为类级变量的类型是非常有益的.谁在乎您是否必须稍后返回并更改本地方法声明?将List暴露给其他方法或库等时,会带来全部好处. (53认同)
  • 这是真的,谢谢你指出了这一点.事实上,现在我考虑一下,从客户端而不是程序员本人的角度来看,我可能更有意义.在这两种情况下,使用`List list = new ArrayList()`是确保不破坏现有代码的问题. (5认同)
  • 执行 `List al = new ArrayList()` 不会让你失去一些可能与 `al` 一起使用的 ArrayList 功能吗? (2认同)

小智 68

这称为编程接口.如果您希望将来转移到List的其他实现,这将非常有用.如果你想要一些方法,ArrayList你需要编程实现ArrayList a = new ArrayList().


Rei*_*ica 22

这在暴露公共接口时也很有用.如果你有这样的方法,

public ArrayList getList();
Run Code Online (Sandbox Code Playgroud)

然后你决定改成它,

public LinkedList getList();
Run Code Online (Sandbox Code Playgroud)

任何正在做的人ArrayList list = yourClass.getList()都需要更改他们的代码.另一方面,如果你这样做,

public List getList();
Run Code Online (Sandbox Code Playgroud)

更改实施不会改变API用户的任何内容.


小智 11

我认为@ tsatiz的答案大多是正确的(编程到接口而不是实现).但是,通过对接口进行编程,您不会丢失任何功能.让我解释.

如果将变量声明为a List<type> list = new ArrayList<type>,则实际上不会丢失ArrayList的任何功能.所有你需要做的就是把你list归结为一个ArrayList.这是一个例子:

List<String> list = new ArrayList<String>();
((ArrayList<String>) list).ensureCapacity(19);
Run Code Online (Sandbox Code Playgroud)

最终我认为tsatiz是正确的,因为一旦你转换为ArrayList,你就不再编码到接口了.但是,最初编写接口代码仍然是一种很好的做法,如果必要的话,如果以后需要,则编写实现代码.

希望有所帮助!

  • 我真的不喜欢这个答案.如果您使用的是ArrayList方法,则应将其定义为ArrayList.如果有人去更改列表实现,他们肯定会对编译失败感到惊讶. (3认同)

ass*_*ias 7

这使您可以编写如下内容:

void doSomething() {
    List<String>list = new ArrayList<String>();
    //do something
}
Run Code Online (Sandbox Code Playgroud)

稍后,您可能希望将其更改为:

void doSomething() {
    List<String>list = new LinkedList<String>();
    //do something
}
Run Code Online (Sandbox Code Playgroud)

无需更改方法的其余部分.

但是,如果你想使用CopyOnWriteArrayList例如,你需要声明它,如果你想使用它的额外方法(例如addIfAbsent),则不需要声明它:

void doSomething() {
    CopyOnWriteArrayList<String>list = new CopyOnWriteArrayList<String>();
    //do something, for example:
    list.addIfAbsent("abc");
}
Run Code Online (Sandbox Code Playgroud)


SHi*_*KiT 5

每当我不想增加问题的复杂性时,我就会使用这种结构.它只是一个列表,不需要说明它是什么类型的列表,因为它与问题无关.我经常在我的大多数解决方案中使用Collection,因为最终,对于软件的其余部分,最重要的是它所拥有的内容,我不想在集合中添加新对象. .

此外,当您认为您可能想要更改正在使用的列表的实现时,可以使用该构造.假设您正在使用带有ArrayList的构造,并且您的问题不是线程安全的.现在,您希望使其成为线程安全的,并且对于部分解决方案,您可以更改为使用Vector.至于该列表的其他用途无关紧要,如果它是AraryList或Vector,只是一个List,则不需要新的修改.


Mou*_*hna 5

我想你问题的核心是为什么 program to an interface, not to an implementation

只是因为一个接口为您提供了更多抽象,并使代码更灵活,更灵活,因为您可以使用相同接口的不同实现(在这种情况下,您可能希望将List实现更改为linkedList而不是ArrayList)没有改变它的客户.