No hay diferencia entre los objetos; Tienes un HashMap<String, Object>
en ambos casos. Hay una diferencia en la interfaz que tiene con el objeto. En el primer caso, la interfaz es HashMap<String, Object>
, mientras que en el segundo es Map<String, Object>
. Pero el objeto subyacente es el mismo.
La ventaja de usar Map<String, Object>
es que puede cambiar el objeto subyacente para que sea un tipo diferente de mapa sin romper su contrato con ningún código que lo esté usando. Si lo declara como HashMap<String, Object>
, debe cambiar su contrato si desea cambiar la implementación subyacente.
Ejemplo: Digamos que escribo esta clase:
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...
}
La clase tiene un par de mapas internos de cadena-> objeto que comparte (a través de métodos de acceso) con subclases. Digamos que lo escribo con HashMap
s para empezar porque creo que esa es la estructura apropiada para usar al escribir la clase.
Más tarde, Mary escribe código subclasificándolo. Ella tiene algo que necesita hacer con ambos things
y moreThings
, naturalmente, lo pone en un método común, y usa el mismo tipo que usé en getThings
/ getMoreThings
al definir su 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...
}
Más tarde, decido que en realidad, es mejor si lo uso en TreeMap
lugar de HashMap
en Foo
. Actualizo Foo
, cambiando HashMap
a TreeMap
. Ahora, SpecialFoo
ya no se compila, porque he roto el contrato: Foo
solía decir que proporcionaba HashMap
s, pero ahora está proporcionando en su TreeMaps
lugar. Así que tenemos que arreglarlo SpecialFoo
ahora (y este tipo de cosas pueden pasar por una base de código).
A menos que tuviera una buena razón para compartir que mi implementación estaba usando un HashMap
(y eso sucede), lo que debería haber hecho fue declarar getThings
y getMoreThings
simplemente regresar Map<String, Object>
sin ser más específico que eso. De hecho, salvo una buena razón para hacer otra cosa, incluso dentro de Foo
lo que probablemente debería declarar things
y moreThings
como Map
, no 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...
}
Tenga en cuenta que ahora estoy usando Map<String, Object>
todo lo que puedo, solo siendo específico cuando creo los objetos reales.
Si hubiera hecho eso, entonces Mary habría hecho esto:
class SpecialFoo extends Foo {
private void doSomething(Map<String, Object> t) { // <== Changed
// ...
}
public void whatever() {
this.doSomething(this.getThings());
this.doSomething(this.getMoreThings());
}
}
... y cambiar Foo
no habría hecho que SpecialFoo
dejara de compilar.
Las interfaces (y las clases base) nos permiten revelar todo lo que sea necesario , manteniendo nuestra flexibilidad bajo las cubiertas para realizar los cambios según corresponda. En general, queremos que nuestras referencias sean lo más básicas posible. Si no necesitamos saber que es un HashMap
, simplemente llámelo a Map
.
Esta no es una regla ciega, pero en general, la codificación de la interfaz más general será menos frágil que la codificación de algo más específico. Si lo hubiera recordado, no habría creado una Foo
que hiciera fracasar a Mary SpecialFoo
. Si Mary hubiera recordado eso, entonces, a pesar de que me equivoqué Foo
, habría declarado su método privado en Map
lugar de HashMap
y el cambio de mi Foo
contrato no habría afectado su código.
A veces no puedes hacer eso, a veces tienes que ser específico. Pero a menos que tenga una razón para estarlo, errar hacia la interfaz menos específica.