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

问题描述:

可能重复:
Why should the interface for a Java class be prefered?多态性:为什么使用“List list = new ArrayList”而不是“ArrayList list = new ArrayList”?

什么时候应该使用

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

ArrayList继承List,所以如果在ArrayList一些功能在List,然后我将失去的某些功能,对吗?编译器会在尝试访问这些方法时发现错误?

+0

哦。对不起。因为我变成了'物体',所以我忘了它。 – hqt 2012-03-24 15:44:13

+6

@duffymo - 这个问题是问为什么'新列表()'不起作用。这是一个完全不同的问题。 – 2012-03-24 18:18:34

+2

它不是一个重复的问题。可能的重复有通用的问题,但具体的细节。我发现标题中的这个问题与实际要求的内容更相关/同步。 – Dexters 2013-05-26 20:31:53

你要做到这一点的主要原因是将你的代码从界面的特定实现中解耦出来。当你写你这样的代码:

List list = new ArrayList(); 

你的代码的其余部分只知道数据是List类型,这是可取的,因为它可以让你的List接口轻松的不同实现之间进行切换。例如,假设你正在编写一个相当大的第三方库,并且说你决定用LinkedList来实现你的库的核心。如果你的图书馆非常依赖访问这些列表中的元素,那么最终你会发现你做出了糟糕的设计决定;你会意识到你应该使用一个ArrayList(它给出O(1)访问时间)而不是LinkedList(它给出O(n)访问时间)。假设你已经编程到一个接口,做出这样的改变很容易。你会简单地改变实例的List从,

List list = new LinkedList(); 

List list = new ArrayList(); 

,你知道,这将工作,因为你写你的代码遵循由List接口提供的合同。另一方面,如果你已经使用LinkedList list = new LinkedList()实现了库的核心,那么做出这样的改变就不那么容易了,因为不能保证其余的代码不能使用方法具体到LinkedList类。总之,选择只是设计问题......但这种设计非常重要(特别是在处理大型项目时),因为它可以让您在不中断的情况下进行特定于实现的更改现有的代码。

+4

明确的解释。 :) – coder247 2012-03-28 07:55:42

+44

很多人没有提及通过ArrayList list = new ArrayList()将它实例化为局部变量是完全可以接受的。在方法签名中使用List接口或作为类级别变量的类型才是真正有益的。谁在乎是否必须稍后再回来并更改本地方法声明?将列表暴露给其他方法或库等时,整个好处就来了。 – GreenieMeanie 2012-03-29 16:28:21

+5

这是真的,谢谢指出。事实上,现在我想到了,从客户的角度而不是程序员自己来看,我可能更有意义。在这两种情况下,使用'List list = new ArrayList()'是确保您不会破坏现有代码的问题。 – 2012-04-01 17:05:42

这被称为编程接口。如果您希望将来转移到List的某些其他实现,这将有所帮助。如果你需要ArrayList中的一些方法,那么你需要编程到ArrayList a = new ArrayList()的实现。

这使你写的东西,如:后来

void doSomething() { 
    List<String>list = new ArrayList<String>(); 
    //do something 
} 

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

void doSomething() { 
    List<String>list = new LinkedList<String>(); 
    //do something 
} 

,而无需改变该方法的其余部分。

但是,如果你想使用一个CopyOnWriteArrayList例如,你需要将其申报为这样的,而不是作为一个列表如果你想使用它的额外的方法(addIfAbsent为例):

void doSomething() { 
    CopyOnWriteArrayList<String>list = new CopyOnWriteArrayList<String>(); 
    //do something, for example: 
    list.addIfAbsent("abc"); 
} 
+1

根据公认的答案@GreenieMeanie评论,这个改变并不重要,因为这是一个局部变量。 – Ram 2016-01-07 09:37:12

只要我不想增加问题的复杂性,我就会使用该构造。这只是一个列表,不需要说明它是什么样的列表,因为这对问题无关紧要。我经常对我的大部分解决方案使用Collection,因为最后,在大多数情况下,对于其他软件,真正重要的是它所包含的内容,而且我不想向Collection中添加新对象。

此外,当您认为您可能想要更改正在使用的列表的实施时,您会使用该构造。假设您使用ArrayList构造,并且您的问题不是线程安全的。现在,您想让线程安全,并且对于您的解决方案的一部分,例如,您可以更改为使用Vector。至于这个列表的其他用途,如果它是一个AraryList或一个Vector,只是一个列表就不会有问题,不需要进行新的修改。

一般而言,您希望根据界面进行编程。这使您可以随时交换实施。 这是非常有用的,特别是当你通过一个你不知道的实现。

但是,在某些情况下,您更愿意使用具体的实现。 例如,当在GWT中序列化时。

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

public ArrayList getList(); 

然后你决定将其改为,

public LinkedList getList(); 

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

public List getList(); 

更改实现不会改变任何API为您的用户。

+0

yourClass.getList()我猜是我碰到过的最实际的用例。非常感谢:) – 2017-05-05 16:38:52

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

很简单,因为一个接口为您提供了更抽象,并使代码 更加灵活和有弹性的变化,因为你可以使用相同的不同 实现接口(在这种情况下,您可能希望将List实现更改为linkedList而不是ArrayList),而无需更改其客户端。

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

如果您声明变量为List<type> list = new ArrayList<type>您实际上不会丢失ArrayList的任何任何功能。您所需要做的就是将您的list下降到ArrayList。这里有一个例子:

List<String> list = new ArrayList<String>(); 
((ArrayList<String>) list).ensureCapacity(19); 

最后,我想tsatiz是正确的,因为一旦你施放到一个ArrayList你不再编码到一个接口。但是,最初编写代码到一个接口仍然是一个很好的做法,如果必须的话,如果它稍后有必要,则编码到实现。

希望有帮助!

+0

这一个是非常容易和清晰。 – CopsOnRoad 2018-01-03 17:13:12