【问题标题】:What is the benefit of polymorphism using Collection interface to create ArrayList object?使用 Collection 接口创建 ArrayList 对象的多态性有什么好处?
【发布时间】:2010-07-28 18:23:13
【问题描述】:

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

假设类 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();

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

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

ArrayList myList = new ArrayList();

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

【问题讨论】:

    标签: java polymorphism


    【解决方案1】:

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

    请注意,在CollectionArrayList 之间,还有另一个抽象级别:List 接口。通常,列表的使用模式与映射、集合或队列的使用模式非常不同。因此,工作所需的收藏类型通常在早期就已确定,并且不会改变。将您的变量声明为 List 可以明确此决定,并为其客户提供有关此集合遵守的合同的有用信息。 Collection OTOH 通常除了迭代其元素之外并没有多大用处。

    【讨论】:

      【解决方案2】:

      写起来可能更常见 List<Something> myList = new ArrayList<Something>(); 比使用 Collection。通常它作为一个列表的某些方面很重要。 Collection 在接受重复元素方面的模糊性,无论它是下面的集合还是列表(或其他任何东西)都可能是一种痛苦。

      除此之外,主要目的是抽象或实现独立性。我真的关心我拥有的列表是ArrayList 还是Vector?可能大部分时间都不会。如果我的代码使用最通用的接口来表达我需要对象执行的操作,那么我的代码会更加灵活。

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

      此外,有时实现类可能不是您可以或不想导入和使用的东西。例如,当使用像 hibernate 这样的持久性提供程序时,实现Set 接口的实际类可能是框架独有的高度专业化的自定义实现,或者它可能是普通的旧HashSet,具体取决于对象的创建方式。你不关心区别,它只是一个Set给你。

      【讨论】:

        【解决方案3】:

        如果您声明ArrayList,我永远不会使用ArrayList 作为左侧的类型。相反,编程到接口,无论是List 还是Collection

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

        附带说明,请考虑使用Generics

        编辑:话虽如此,泛型也引入了一些陷阱。

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

        【讨论】:

          【解决方案4】:

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

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

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

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

          【讨论】:

            【解决方案5】:

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

            考虑:

            ArrayList words = new ArrayList();
            sort(words);
            removeNames(words);
            
            public void sort(Collection c)  ... blah blah blah
            
            public void removeNames(List words) ...  
            

            我本可以将“单词”的类型替换为列表。它没有 对我的程序的可读性或行为产生任何影响。 我无法将“单词”定义为对象。太笼统了。

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

            ArrayList words = new ArrayList();
            
            // this line will cause a compilation error.
            sort(words);
            
            public void sort(LinkedList c)  ... blah blah blah
            

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

            编写 java 库的主要技巧之一是确定类型 的方法参数。你想成为多一般的人?

            【讨论】:

              【解决方案6】:

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

              【讨论】:

                【解决方案7】:

                通过声明和使用myList 作为集合,您隐藏了您正在做出的实现选择(在这种情况下,它表示为一个ArrayList)。一般来说,这意味着任何依赖于你的代码的东西都将只依赖于 myList 作为一个 Collection,而不是特别是一个 ArrayList。这样,如果您以后出于任何原因决定将其表示为其他东西(一个集合?一个链接列表?),您不会破坏其他任何东西。

                【讨论】:

                • Map 不扩展/实现Collection
                【解决方案8】:

                CollectionArrayList 的超类型。如果您只需要Collection 提供的功能,这是一种很好的做法,因为您在变量声明 中明确指出了您需要的功能。您在 initialization 中选择 ArrayList 是无关紧要的(尽管是一个很好的默认选择);声明它是 Collection 会告诉您和任何未来的编码员您真正关心的合同。

                【讨论】:

                • @Jacob Tomaw 这是一个很好的做法,因为您在变量 declaration 中明确指出了您需要的功能。您在 initialization 中选择 ArrayList 是无关紧要的(尽管是一个很好的默认选择);声明它是 Collection 会告诉你和任何未来的程序员你关心的合约是什么。
                • 您可以通过将答案包含在答案中来显着改进您的答案,而不是假设每个人都知道为什么这是一个好习惯。我认为大多数阅读此问题的人不会有你或我的经验。
                猜你喜欢
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 2012-10-09
                • 1970-01-01
                • 2012-01-21
                • 1970-01-01
                • 2019-11-13
                相关资源
                最近更新 更多