Reason for - List list = new ArrayList(); - List list = new ArrayList() 的原因。
当有人写出类似于List list = new ArrayList();
这样的代码时,他/她试图遵循一项基本的面向对象设计原则,即:
针对接口编程,而不是具体实现
我在我的博客文章中解释了这个原则。请查看Class Inheritance VS Interface Inheritance
部分。
总结一下文章,在使用父类型的引用来引用子类型的实例时,你会获得很大的灵活性。例如,如果将来需要更改子类型的实现,你可以轻松地进行操作,而不需要改变很多代码。
考虑下面的方法:
public void DoSomeStuff(Super s) { s.someMethod(); }
以及对该方法的调用:
DoSomeStuff(new Sub());
现在,如果你将来需要改变someMethod
内部的逻辑,你可以通过声明一个Super
的新子类型,比如NewSubType
,并在该实现中改变逻辑来轻松实现。这样,你将不需要触碰使用该方法的其他现有代码。你仍然可以以以下方式使用你的DoSomeStuff
方法:
DoSomeStuff(new NewSubType());
如果你将DoSomeStuff
的参数声明为Sub
,那么你还必须改变它的实现:
DoSomeStuff(NewSubType s) { s.someMethod(); }
而且这可能会在其他地方产生连锁/冒泡效应。
就你的集合示例而言,这使得你可以在不费力的情况下更改变量所指向的列表实现。你可以轻松地在ArrayList
的位置使用LinkedList
。
“针对接口编程,而不是具体实现”是一个很好的建议。但是,这并不是一个理由去使用List<String> list = new ArrayList<String>();
,因为a)该代码仍然与ArrayList绑定在一起,因为构造函数,b)这更多的是关于API设计而不是私有实现。这是你从一开始就学到的Java的一些东西,没有真正的理由进行解释。当然,答案仍然是正确的。(+1)
但是,他的观点仍然成立。通过更改参数,您可以交换功能。这支持多种很好的代码特性,比如依赖注入、松散耦合和可替代性。
:这个原则的一个结果是鼓励您用接口来引用具体实例。这就是为什么我认为它在这里是相关的。
:完全同意你的观点。
在原始问题中,没有参数。只有List<Foo> l = new ArrayList<Foo>();
如果实现发生变化,那么这一行也会发生变化。如果是ArrayList<Foo> l = new ArrayList<Foo>();
也不会有任何不同。如果列表是一个外部依赖项(以某种方式注入),那么整个情况就改变了。在这种情况下,编程到接口将产生不同,并且强烈建议这样做。
:正如我所说,基本思想是使用超类型的引用来操作实例。如果仅使用List
中定义的方法就足以操作该实例,那么最好这样做。谁知道,也许将来他可能决定将其作为属性而不是局部变量,并且他还可能决定注入一个与ArrayList
不同的实现。当然,在这里引用List
将给他带来优势。这里重要的是“实践”。
原因:为了实现代码的松耦合,可以在任何时候更换list的数据类型,只要实现了List接口即可。
解决方法:创建一个List对象list,并将其类型声明为List接口类型,通过这种方式可以在任何时候更换list的具体实现类。
代码示例:
Listlist; public SomeConstructor() { // 在这个地方,你可以将其更换为任何类型的对象 list = new ArrayList (); list = new LinkedList (); list = new AttributeList (); }
上述代码将通过将list对象的类型声明为List接口类型,使得代码与list的具体实现类解耦,只需知道list具有add等方法即可。这被称为松耦合。