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 HashMap
s 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 things
e moreThings
, portanto, naturalmente, coloca isso em um método comum, e ela usa o mesmo tipo que eu usei getThings
/ getMoreThings
ao 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 TreeMap
em vez de HashMap
no Foo
. Eu atualizo Foo
, mudando HashMap
para TreeMap
. Agora, SpecialFoo
não compila mais, porque eu quebrei o contrato: Foo
costumava dizer que fornecia HashMap
s, mas agora está fornecendo TreeMaps
. Portanto, temos que corrigir SpecialFoo
agora (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 getThings
e getMoreThings
apenas 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 Foo
eu provavelmente deveria declarar things
e moreThings
como 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 Foo
não teria feito SpecialFoo
parar 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 Foo
que deixasse Mary com defeito SpecialFoo
. Se Mary tivesse se lembrado disso, mesmo que eu errei Foo
, ela teria declarado seu método privado em Map
vez de HashMap
e meu Foo
contrato 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.