对象之间没有区别。有一个与您的对象的接口的差异。在第一种情况下,接口是HashMap,而在第二种情况下是Map< String,Object> ;.基础对象,但是,是相同的。
使用Map是你可以改变底层对象是一种不同类型的地图,而不会破坏你的合同使用它的任何代码。如果你声明它为HashMap,如果你想改变底层的实现,你必须改变你的契约。
示例:假设我写这个类:
class Foo {
private HashMap things;
private HashMap moreThings;
protected HashMap getThings() {
return this.things;
}
protected HashMap getMoreThings() {
return this.moreThings;
}
public Foo() {
this.things = new HashMap();
this.moreThings = new HashMap();
}
// ...more...
}
该类有一些内部映射的string->对象,它与(通过访问器方法)与子类共享。让我们说,我用HashMaps写它开始,因为我认为这是编写类时使用的适当的结构。
后来,玛丽写了代码子类化它。她有一些东西,她需要做的两件事情和moreThings,所以自然她提出了一个通用的方法,她使用相同的类型我使用getThings / getMoreThings定义她的方法时:
class SpecialFoo extends Foo {
private void doSomething(HashMap t) {
// ...
}
public void whatever() {
this.doSomething(this.getThings());
this.doSomething(this.getMoreThings());
}
// ...more...
}
后来,我决定,实际上,最好是如果我在Foo中使用TreeMap而不是HashMap。我更新Foo,将HashMap更改为TreeMap。现在,SpecialFoo不再编译,因为我违反了合同:Foo曾经说它提供了HashMaps,但现在它提供了TreeMaps。所以我们必须修复SpecialFoo现在(这种事情可以通过代码库)。
除非我有一个真正好的共享理由,我的实现是使用一个HashMap(并确实发生),我应该做的是声明getThings和getMoreThings作为只返回Map没有比这更具体的。事实上,除非有好的理由做别的事情,即使在Foo中我应该声明的事情和moreThings作为Map,而不是HashMap / TreeMap:
class Foo {
private Map things; // <== Changed
private Map moreThings; // <== Changed
protected Map getThings() { // <== Changed
return this.things;
}
protected Map getMoreThings() { // <== Changed
return this.moreThings;
}
public Foo() {
this.things = new HashMap();
this.moreThings = new HashMap();
}
// ...more...
}
注意我现在如何使用Map到处我可以,只有当我创建实际的对象时具体。
如果我这样做,那么玛丽会这样做:
class SpecialFoo extends Foo {
private void doSomething(Map t) { // <== Changed
// ...
}
public void whatever() {
this.doSomething(this.getThings());
this.doSomething(this.getMoreThings());
}
}
…并且更改Foo不会使SpecialFoo停止编译。
接口(和基类)让我们只显示必要的内容,保持我们的灵活性,以适当地进行更改。一般来说,我们希望我们的引用尽可能基本。如果我们不需要知道它是一个HashMap,只需将它称为一个Map。
这不是一个盲目的规则,但一般来说,编码到最通用的接口比编码到更具体的东西要脆弱。如果我记得,我不会创建一个Foo,设置玛丽的失败与SpecialFoo。如果玛丽记住了,那么即使我搞砸了Foo,她会使用Map而不是HashMap来声明它的私有方法,我改变Foo的合同不会影响她的代码。
有时你不能这样做,有时你必须具体。但除非你有理由,否则向最不具体的接口。