我研究了多态,并了解它可以像下面那样进行动态方法绑定。
假设类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文档以及在线教程,但仍然不太清楚.. 谁能给我一些解释呢?
答案 0 :(得分:10)
如果您将myList
声明为ArrayList
,则会修复其具体类型。使用它的每个人都将依赖于这种具体类型,并且很容易(甚至是无意中)调用特定于ArrayList
的方法。如果以后某个时候您决定将其更改为例如LinkedList
或CopyOnWriteArrayList
,您需要重新编译 - 甚至可能更改 - 客户端代码。接口编程消除了这种风险。
请注意,在Collection
和ArrayList
之间,还有另一个抽象级别:List
接口。通常,列表的使用模式与地图,集合或队列的使用模式非常不同。因此,工作所需的集合类型通常在早期就已确定,并且不会发生变化。将您的变量声明为List
可以明确这一决定,并为其客户提供有关此集合服从的合同的有用信息。 Collection
OTOH通常不仅仅是迭代它的元素非常有用。
答案 1 :(得分:4)
写作可能更常见
List<Something> myList = new ArrayList<Something>();
而不是Collection
。通常,它作为列表的某些方面是重要的。 Collection
关于接受重复元素的模糊性,无论是下面的集合还是列表(或其他),都可能是一种痛苦。
除此之外,主要目的是抽象或实现独立性。如果我的列表是ArrayList
或Vector
,我真的关心吗?可能不是大部分时间。如果我的代码使用表达我需要对象的最通用的接口,那么我的代码会更灵活。
简单的例子是,假设您使用所有ArrayList
编写程序,然后它需要支持多个用户,因此为了线程安全,您希望将所有ArrayList
更改为{ {1}}的RS。如果您一直在传递类型Vecto
的引用,则必须更改每个地方的所有用法。如果您一直在传递类型ArrayList
的引用,则只需更改创建它们的位置。
此外,有时实现类可能不是您可以或想要导入和使用的东西。例如,当使用像hibernate这样的持久性提供程序时,实现List
接口的实际类可能是框架特有的高度专业化的自定义实现,或者它可能是普通的Set
,具体取决于如何对象被创建了。你不关心差异,对你来说只是HashSet
。
答案 2 :(得分:3)
如果你宣布ArrayList
,我就不会在左侧使用ArrayList
作为类型。而是编程到界面,无论是List
还是Collection
。
请注意,如果您将方法声明为Collection
,则可以传递List
或Set
。
作为旁注,请考虑使用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)
Collection
是ArrayList
的超类型。如果您只需要Collection
提供的功能,那么这是一个很好的做法,因为您在变量声明中明确指出了您需要的功能。您在初始化中选择ArrayList
是无关紧要的(虽然这是一个很好的默认选择);声明它是一个Collection
告诉你和任何未来的编码器确切地说你关心的合同。
答案 7 :(得分:0)
通过声明并使用myList
作为集合,您将隐藏正在进行的实现选择(在这种情况下,它表示为ArrayList)。通常,这意味着任何依赖于您的代码的东西都只依赖于myList表现为Collection,而不是特别是ArrayList。那样的话,如果你因为某种原因决定稍后将其表示为其他东西(Set?A Linked List?),那么你就不会破坏其他任何东西。