使用Collection接口创建ArrayList对象的多态性有什么好处?

时间:2010-07-28 18:23:14

标签: java polymorphism

我研究了多态,并了解它可以像下面那样进行动态方法绑定。

假设类Animal是抽象类。

public class AnimalReference
{
  public static void main(String args[])
  Animal ref                 // set up var for an Animal
  Cow aCow = new Cow("Bossy"); // makes specific objects
  Dog aDog = new Dog("Rover");

  // now reference each as an Animal
  ref = aCow; ref.speak();
  ref = aDog; ref.speak();
}

我曾经创建过ArrayList的实例,如:

ArrayList myList = new ArrayList();

但通常我认为人们写道:

Collection myList = new ArrayList();

所以我的困惑是宣称收藏的好处是什么?另外我不知道你可以在“myList”前面有“Collection”(这是一个非抽象类的接口)。

为什么说这是不好的做法:

ArrayList myList = new ArrayList();

我阅读了Collection接口和ArrayList Java文档以及在线教程,但仍然不太清楚.. 谁能给我一些解释呢?

8 个答案:

答案 0 :(得分:10)

如果您将myList声明为ArrayList,则会修复其具体类型。使用它的每个人都将依赖于这种具体类型,并且很容易(甚至是无意中)调用特定于ArrayList的方法。如果以后某个时候您决定将其更改为例如LinkedListCopyOnWriteArrayList,您需要重新编译 - 甚至可能更改 - 客户端代码。接口编程消除了这种风险。

请注意,在CollectionArrayList之间,还有另一个抽象级别:List接口。通常,列表的使用模式与地图,集合或队列的使用模式非常不同。因此,工作所需的集合类型通常在早期就已确定,并且不会发生变化。将您的变量声明为List可以明确这一决定,并为其客户提供有关此集合服从的合同的有用信息。 Collection OTOH通常不仅仅是迭代它的元素非常有用。

答案 1 :(得分:4)

写作可能更常见 List<Something> myList = new ArrayList<Something>();而不是Collection。通常,它作为列表的某些方面是重要的。 Collection关于接受重复元素的模糊性,无论是下面的集合还是列表(或其他),都可能是一种痛苦。

除此之外,主要目的是抽象或实现独立性。如果我的列表是ArrayListVector,我真的关心吗?可能不是大部分时间。如果我的代码使用表达我需要对象的最通用的接口,那么我的代码会更灵活。

简单的例子是,假设您使用所有ArrayList编写程序,然后它需要支持多个用户,因此为了线程安全,您希望将所有ArrayList更改为{ {1}}的RS。如果您一直在传递类型Vecto的引用,则必须更改每个地方的所有用法。如果您一直在传递类型ArrayList的引用,则只需更改创建它们的位置。

此外,有时实现类可能不是您可以或想要导入和使用的东西。例如,当使用像hibernate这样的持久性提供程序时,实现List接口的实际类可能是框架特有的高度专业化的自定义实现,或者它可能是普通的Set,具体取决于如何对象被创建了。你不关心差异,对你来说只是HashSet

答案 2 :(得分:3)

如果你宣布ArrayList,我就不会在左侧使用ArrayList作为类型。而是编程到界面,无论是List还是Collection

请注意,如果您将方法声明为Collection,则可以传递ListSet

作为旁注,请考虑使用Generics

编辑:话虽如此,Generics还介绍了一些问题。

List<Animal>可以存储ArrayList<Animal>,但不能 ArrayList<Cat>。您需要List<? extends Animal>来存储ArrayList<Cat>

答案 3 :(得分:1)

首先,继承和接口之间存在显着差异。短暂的回溯历史:在普通的旧c ++中你可以从多个类继承。如果“Transformer”类继承自“Vehicle”和“Human”两者,它们实现了一个名为“MoveForward”的方法,则会产生负面影响。如果在“Transformer”类上调用此方法,则实例使用哪个函数dshoud? “人”还是“车”实施?为了解决这个问题,java,c#,...引入了接口。接口是您和其他人之间的合同。您对功能做出了承诺,但合同没有为您的类实现任何逻辑(以防止Transformer.MoveForward“问题)。

多态性一般意味着某些东西可以以不同的方式出现。 “变形金刚”可以是“车辆”和“人”。根据您的语言(我使用C#),您可以为“MoveForward”实现不同的行为,具体取决于您要实现的合同。

使用接口而不是具体实现具有几个优点。首先,您可以在不更改代码的情况下切换实现(用于Google查找的依赖注入;)。其次,您可以使用测试框架和模拟框架更轻松地测试代码。第三,使用最通用的接口数据交换(如果您只想对值进行交互,则使用列表的枚举器instad)是一种很好的做法。

希望这有助于理解接口的优点。

答案 4 :(得分:1)

您在局部变量声明中使用的类型(如在ArrayList中) 例子)通常不那么重要。所有你必须确保的是 myList的类型(名称'myList'左侧的单词)具有 比任何方法参数的类型更具体 我的列表。

考虑:

ArrayList words = new ArrayList();
sort(words);
removeNames(words);

public void sort(Collection c)  ... blah blah blah

public void removeNames(List words) ...  

我本可以将'words'的类型替换为List。它没有 对我的程序的可读性或行为有任何不同。 我无法将'words'定义为Object。这太笼统了。

在相关说明中,当您定义公共方法时,应该给出 仔细考虑方法的参数类型,因为 这对调用者可以传入的内容有直接影响。如果我定义了 不同的排序:

ArrayList words = new ArrayList();

// this line will cause a compilation error.
sort(words);

public void sort(LinkedList c)  ... blah blah blah

排序的定义现在非常严格。在第一个例子中, sort方法允许任何对象作为参数,只要它 实现集合。在第二个例子中,sort只允许a LinkedList,它不接受任何其他东西(ArrayLists,HashSets, TreeSets和许多其他)。 sort方法可以使用的场景 使用现在非常有限。这可能是有充分理由的;该 sort的实现可能依赖于LinkedList的一个特性 数据结构。如果人们使用这种方式定义排序是很糟糕的 这段代码需要一种适用于其他事物的排序算法 LinkedLists。

编写java库的主要技巧之一是决定类型 方法参数。你想要多么普遍?

答案 5 :(得分:1)

嗯,一个arraylist有一个动态的大小。该集合没有编译时检查,必须进行强制转换。集合仅包含引用的对象。您可以将集合视为“包”。并且可以在整个对象上执行操作。我也确信与ArrayList相比,集合的搜索时间很短,但不是正面的。 ArrayLists具有更多可以调用的功能和方法。

答案 6 :(得分:0)

CollectionArrayList的超类型。如果您只需要Collection提供的功能,那么这是一个很好的做法,因为您在变量声明中明确指出了您需要的功能。您在初始化中选择ArrayList是无关紧要的(虽然这是一个很好的默认选择);声明它是一个Collection告诉你和任何未来的编码器确切地说你关心的合同。

答案 7 :(得分:0)

通过声明并使用myList作为集合,您将隐藏正在进行的实现选择(在这种情况下,它表示为ArrayList)。通常,这意味着任何依赖于您的代码的东西都只依赖于myList表现为Collection,而不是特别是ArrayList。那样的话,如果你因为某种原因决定稍后将其表示为其他东西(Set?A Linked List?),那么你就不会破坏其他任何东西。