我建立的如下地图之间有什么区别(在另外一个问题中,人们彷佛能够互换使用它们,我想知道它们是否/如何不一样): this
HashMap<String, Object> map = new HashMap<String, Object>(); Map<String, Object> map = new HashMap<String, Object>();
您建立相同的地图。 编码
可是您能够在使用时弥补差别。 在第一种状况下,您将可以使用特殊的HashMap方法(但我不记得任何人真的有用),而且您能够将其做为HashMap参数传递: spa
public void foo (HashMap<String, Object) { ... } ... HashMap<String, Object> m1 = ...; Map<String, Object> m2 = ...; foo (m1); foo ((HashMap<String, Object>)m2);
对象之间没有区别; 在两种状况下HashMap<String, Object>
您都有HashMap<String, Object>
。 与对象之间的接口有所不一样。 在第一种状况下,接口是HashMap<String, Object>
,而在第二种状况下,接口是Map<String, Object>
。 可是底层对象是相同的。 code
使用Map<String, Object>
的优势在于,您能够将基础对象更改成另外一种类型的映射,而不会违反使用该映射的任何代码的约定。 若是将其声明为HashMap<String, Object>
, HashMap<String, Object>
更改基础实现,则必须更改合同。 对象
示例:假设我编写了此类: 接口
class Foo { private HashMap<String, Object> things; private HashMap<String, Object> moreThings; protected HashMap<String, Object> getThings() { return this.things; } protected HashMap<String, Object> getMoreThings() { return this.moreThings; } public Foo() { this.things = new HashMap<String, Object>(); this.moreThings = new HashMap<String, Object>(); } // ...more... }
该类有一些string-> object的内部映射,它与子类共享(经过访问器方法)。 假设我首先使用HashMap
编写它,由于我认为这是编写类时要使用的适当结构。 ci
后来,玛丽编写了将其子类化的代码。 她须要同时处理things
和moreThings
,所以天然而然地将其放在一个通用方法中,而且在定义她的方法时,她使用与getThings
/ getMoreThings
相同的类型: get
class SpecialFoo extends Foo { private void doSomething(HashMap<String, Object> t) { // ... } public void whatever() { this.doSomething(this.getThings()); this.doSomething(this.getMoreThings()); } // ...more... }
稍后,我决定实际上,最好是在Foo
使用TreeMap
而不是HashMap
。 我更新Foo
,将HashMap
更改成TreeMap
。 如今, SpecialFoo
再也不编译,由于我违反了合同: Foo
曾经说它提供了HashMap
,可是如今提供了TreeMaps
。 所以,咱们如今必须修复SpecialFoo
(这种事情可能会在代码库中引发涟漪)。 string
除非我有一个很好的理由要分享个人实现正在使用HashMap
(而且确实发生了),不然我应该作的就是将getThings
和getMoreThings
声明为仅返回Map<String, Object>
而没有比这更具体的声明。 实际上,除非有充分的理由作其余事情,即便在Foo
我也应该将things
和moreThings
things
声明为Map
,而不是HashMap
/ TreeMap
: 编译
class Foo { private Map<String, Object> things; // <== Changed private Map<String, Object> moreThings; // <== Changed protected Map<String, Object> getThings() { // <== Changed return this.things; } protected Map<String, Object> getMoreThings() { // <== Changed return this.moreThings; } public Foo() { this.things = new HashMap<String, Object>(); this.moreThings = new HashMap<String, Object>(); } // ...more... }
请注意,我如今如何在全部可能的地方使用Map<String, Object>
,仅在建立实际对象时才具体使用。
若是我这样作了,那么玛丽就会作到这一点:
class SpecialFoo extends Foo { private void doSomething(Map<String, Object> t) { // <== Changed // ... } public void whatever() { this.doSomething(this.getThings()); this.doSomething(this.getMoreThings()); } }
...而且更改Foo
不会使SpecialFoo
中止编译。
接口(和基类)使咱们仅显示必要的内容,所以能够灵活地进行更改。 总的来讲,咱们但愿参考文献尽量基本。 若是咱们不须要知道它是HashMap
,只需将其称为Map
。
这不是一个盲目的规则,但总的来讲, 与最特定的接口编码相比,对最通用的接口进行编码将不那么困难。 若是我还记得这一点,那么我不会建立一个让Mary因SpecialFoo
失败而建立的Foo
。 若是Mary记得这一点,那么即便我搞砸了Foo
,她也会使用Map
而不是HashMap
声明她的私有方法,而且我更改Foo
的合同不会影响她的代码。
有时候你作不到,有时候你必需要具体。 可是除非有理由,不然请针对最不特定的界面。
在第二个示例中,“ map”引用的类型为Map
,这是由HashMap
(以及其余类型的Map
)实现的接口。 这个接口是一个约定,表示对象将键映射到值并支持各类操做(例如put
和get
)。 它没有提到 Map
的实现 (在本例中为HashMap
)。
一般首选第二种方法,由于您一般不但愿使用Map
或经过API定义将特定的Map实现公开给方法。
Map是Interface,而Hashmap是实现该接口的类。
所以,在此实现中,您将建立相同的对象
正如TJ Crowder和Adamski所指出的,一种参考是接口,另外一种是接口的特定实现。 根据Joshua Block的说法,您应该始终尝试对接口进行编码,以使您可以更好地处理对基础实现的更改-即,若是HashMap忽然对于您的解决方案不理想,而且您须要更改Map的实现,那么您仍然可使用Map接口,并更改实例化类型。