Não há diferença entre os objetos; você tem um HashMap<String, Object>nos dois casos. Há uma diferença na interface que você tem para o objeto. No primeiro caso, a interface é HashMap<String, Object>, enquanto no segundo é Map<String, Object>. Mas o objeto subjacente é o mesmo.
A vantagem de usar Map<String, Object>é que você pode alterar o objeto subjacente para ser um tipo diferente de mapa sem quebrar seu contrato com qualquer código que o esteja usando. Se você o declarar como HashMap<String, Object>, precisará alterar seu contrato se desejar alterar a implementação subjacente.
Exemplo: Digamos que eu escreva esta classe:
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...
}
A classe possui alguns mapas internos de string-> objeto que ele compartilha (por meio de métodos acessadores) com subclasses. Digamos que eu escreva com HashMaps para começar, porque acho que essa é a estrutura apropriada para usar ao escrever a classe.
Mais tarde, Maria escreve código subclassificando-o. Ela tem algo que precisa fazer com ambos thingse moreThings, portanto, naturalmente, coloca isso em um método comum, e ela usa o mesmo tipo que eu usei getThings/ getMoreThingsao definir seu método:
class SpecialFoo extends Foo {
private void doSomething(HashMap<String, Object> t) {
// ...
}
public void whatever() {
this.doSomething(this.getThings());
this.doSomething(this.getMoreThings());
}
// ...more...
}
Mais tarde, eu decido que na verdade, é melhor se eu usar TreeMapem vez de HashMapno Foo. Eu atualizo Foo, mudando HashMappara TreeMap. Agora, SpecialFoonão compila mais, porque eu quebrei o contrato: Foocostumava dizer que fornecia HashMaps, mas agora está fornecendo TreeMaps. Portanto, temos que corrigir SpecialFooagora (e esse tipo de coisa pode se espalhar por uma base de código).
A menos que eu tenha realmente um bom motivo para compartilhar que minha implementação estava usando um HashMap(e isso acontece), o que eu deveria ter feito era declarar getThingse getMoreThingsapenas retornar Map<String, Object>sem ser mais específico do que isso. De fato, exceto por uma boa razão para fazer outra coisa, mesmo dentro de Fooeu provavelmente deveria declarar thingse moreThingscomo Map, não 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...
}
Observe como agora estou usando em Map<String, Object>todos os lugares que posso, apenas sendo específico quando crio os objetos reais.
Se eu tivesse feito isso, Mary teria feito isso:
class SpecialFoo extends Foo {
private void doSomething(Map<String, Object> t) { // <== Changed
// ...
}
public void whatever() {
this.doSomething(this.getThings());
this.doSomething(this.getMoreThings());
}
}
... e mudar Foonão teria feito SpecialFooparar de compilar.
As interfaces (e classes de base) nos permitem revelar apenas o necessário , mantendo nossa flexibilidade oculta para fazer as alterações necessárias. Em geral, queremos que nossas referências sejam o mais básicas possível. Se não precisamos saber que é um HashMap, basta chamá-lo de Map.
Essa não é uma regra cega, mas, em geral, a codificação para a interface mais geral será menos frágil do que a codificação para algo mais específico. Se eu tivesse me lembrado disso, não teria criado uma Fooque deixasse Mary com defeito SpecialFoo. Se Mary tivesse se lembrado disso, mesmo que eu errei Foo, ela teria declarado seu método privado em Mapvez de HashMape meu Foocontrato de mudança não teria afetado seu código.
Às vezes você não pode fazer isso, às vezes você precisa ser específico. Mas, a menos que você tenha um motivo para estar, erre na interface menos específica.