我见过很多这样的代码:
List list = new ArrayList();
为什么人们采用EDOCX1(和其他类)的父类而不是生成对象的类型?
这会降低性能吗?或者为什么要有人这样做?
- "表演"的固定是什么?90%的时候,这是写代码最不重要的方面!
- stackoverflow.com/questions/383947/…
- 你也可以看看这个:stackoverflow.com/questions/17459553/…
- @约阿希姆绍尔也许最简单的事情是,如果我们告诉新手,这大大提高了表现,就像在"每个人都知道…"
- 如果我看不到程序中某件事情的原因,我还应该问什么?我不会说采用父引用会给您提供一个更"可读"的代码。但现在我有了答案,谢谢大家。
- @trudler:它传达的意图是:List x = new ArrayList()意味着你不需要ArrayList的任何特殊功能,你"承诺"只"使用"List接口公开的功能。进一步说,可能是Collection x = new ArrayList()甚至Iterable x = new ArrayList()。
当有人编写这样的代码时,他/她试图遵循一个基本的OO设计原则,即-
Program to an interface, not to a concrete implementation
我在我的一篇博文中解释了这个原则。查看Class Inheritance VS Interface Inheritance部分。
为了总结文章,当您使用父类型的引用引用子类型的实例时,可以获得很大的灵活性。例如,如果将来需要更改子类型实现,则可以轻松地进行更改,而无需更改大部分代码。
考虑以下方法-
1 2 3
| public void DoSomeStuff(Super s) {
s.someMethod();
} |
以及对该方法的调用-
现在,如果您需要更改someMethod中的逻辑,您可以通过声明Super的新子类型(比如NewSubType)并更改该实现中的逻辑来轻松完成。通过这种方式,您将永远不必接触使用该方法的其他现有代码。您仍然可以用以下方法使用您的DoSomeStuff方法-
1
| DoSomeStuff(new NewSubType()); |
如果您声明DoSomeStuff的参数为Sub的参数,那么您也必须更改它的实现。-
1 2 3
| DoSomeStuff(NewSubType s) {
s.someMethod();
} |
它也可能会连锁/泡沫到其他几个地方。
就您的收集示例而言,这允许您更改变量指向的列表实现,而无需太多麻烦。您可以很容易地使用LinkedList代替ArrayList。
- "程序到接口,而不是具体实现"是一个很好的建议。但是,执行List list = new ArrayList();并不是一个真正的参数,因为a)由于构造函数的原因,此代码仍然绑定到arraylist;b)更多的是API设计,而不是私有实现。这是你从一开始就学到的Java的东西,这并不是很有道理。通常,实现提供了一些细节,如果您坚持使用接口,就不会看到这些细节。当然,答案还是不错的。(+ 1)
- 但是,他的论点仍然有效。您可以通过更改参数来交换功能。这支持几个好的代码特性,例如依赖注入、松耦合和可替换性。
- @AtamanRoman:这个原则的一个结果鼓励您在接口方面引用具体的实例。这就是为什么我认为它在这里是相关的。
- @克里斯:完全同意你的看法。
- 在最初的问题中,没有争论。只有EDOCX1[1]如果实现发生变化,该行就会发生变化。如果是ArrayList l = new ArrayList();就不会有什么不同了。如果列表是外部依赖项(以某种方式注入),则整个情况都会发生变化。在这种情况下,对接口进行编程会产生影响,强烈建议这样做。
- @阿塔曼罗曼:正如我所说,基本的想法是用超类型的引用来操纵一个实例。如果只有在List中定义的方法足以操纵该实例,那么最好这样做。谁知道将来他可能会决定把它变成一个属性而不是局部变量,他也可能决定注入一个不同于ArrayList的实现。当然,提到江户十一〔三〕会给他一个优势。这是很重要的练习。
这意味着您可以在任何时候用实现List接口的任何东西交换List类型,而不是创建只能使用ArrayList的刚性模型。例如:
1 2 3 4 5 6 7 8 9
| private List<String> list;
public SomeConstructor()
{
// At this point, you can make it any type of object you want.
list = new ArrayList<String>();
list = new LinkedList<String>();
list = new AttributeList<String>();
} |
这将使abstract您使用List对象的代码远离具体的对象类型List等细节。它只需要知道它有add方法等,这叫做松耦合。
当你写:
1
| List<String> list = new ArrayList<String>(); |
然后确定只使用接口List的功能。(ArrayList实现List,所以List更灵活)。使用它,可以在将来将ArrayList更改为其他类型(如LinkedList)。
要解决问题:
为了获得更大的灵活性,您可以启动接口List:
所以如果你不需要所有的ArrayList,只使用List。
你可以这样写:List = Arrays.asList("aa","bb","cc")。
当然,更少的功能可以帮助提高性能。如您所知,如果您想使用多线程应用程序,请使用Vector,但它会降低您的性能。
从这里带走
- 来源请求:)
- wilsonmar.com/1arrays.htm(网址:wilsonmar.com/1arrays.htm)
- @马克西姆舒斯丁:好画面!这是否意味着,我应该将"abstractmap"作为所有映射集合对象的引用?
- @trudler:不,AbstractMap是一个您不应该真正关心的实现细节:您要么使用Map接口引用映射,要么使用更具体的接口(如NavigableMap)。在极少数情况下,您可能需要特定的实现类型,但是除了扩展它之外,很少有人引用AbstractMap本身。
因为方法不需要知道您使用的列表实现。
一个方法只需要知道这是一个列表。
方法仍然可以使用。
总是编程到一个接口,而不是具体的实现。(在本例中为列表)
通常情况下,最好使用接口类(在本例中为List),这样,如果需求发生变化,任何列表实现稍后都可以用最小的麻烦替换。
虽然ArrayList可能支持不在List接口上的某些方法,但此声明清楚地表明,在这种情况下,这些额外的方法是不相关的。
1
| List<String> list = new ArrayList<String>(); |
在集合框架中,List是接口,而ArrayList是实现接口。您这样做的主要原因是将代码与接口的特定implementation分离,如果您希望在将来转移到List的其他实现,这将很有帮助。