No hay diferencia entre los objetos; tiene un HashMap<String, Object>
en ambos casos. Hay una diferencia en la interfaz que tiene para 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: Supongamos 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 String> objeto que comparte (a través de métodos de acceso) con subclases. Digamos que lo escribo con HashMap
s para comenzar porque creo que esa es la estructura apropiada para usar al escribir la clase.
Más tarde, Mary escribe el código subclasificándolo. Ella tiene algo que tiene que ver tanto con things
y moreThings
, por lo que, naturalmente, ella pone que en un método común, y se utiliza el mismo tipo que utilicé en getThings
/getMoreThings
la hora de 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, decida que en realidad, es mejor si uso TreeMap
en lugar de HashMap
en Foo
. Actualizo Foo
, cambiando HashMap
a TreeMap
. Ahora, SpecialFoo
no compila más, porque he roto el contrato: Foo
solía decir que proporcionaba HashMap
s, pero ahora proporciona TreeMaps
en su lugar. Entonces, tenemos que reparar SpecialFoo
ahora (y este tipo de cosas puede afectar a través de una base de código).
menos que tuviera una buena razón para compartir que mi aplicación se utiliza una HashMap
(y eso sucede), lo que debería haber hecho era declaran getThings
y getMoreThings
como se acaba de regresar Map<String, Object>
sin ser más específico que eso.De hecho, salvo una buena razón para hacer algo más, incluso dentro de Foo
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...
}
Nota cómo estoy ahora usando Map<String, Object>
todas partes puedo, sólo se siendo específico cuando creo los objetos reales.
Si hubiera hecho eso, entonces María 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 cambiando Foo
no habría hecho SpecialFoo
parada de compilación.
Las interfaces (y las clases base) nos permiten revelar solo tanto como sea necesario, manteniendo nuestra flexibilidad debajo de las fundas para hacer los cambios que correspondan. En general, queremos que nuestras referencias sean lo más básicas posible. Si no necesitamos saber que es un HashMap
, simplemente llámelo como Map
.
Esto no es una regla ciega, pero en general, que codifica la interfaz más general va a ser menos frágil que la codificación a algo más específico. Si hubiera recordado eso, no habría creado un Foo
que configuró Mary para el fracaso con SpecialFoo
. Si María se había acordado que, a continuación, a pesar de que la pata Foo
, habría declarado su método privado con Map
en lugar de HashMap
y mi contrato cambiar Foo
's 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 estar, errar hacia la interfaz menos específica.
por lo que la única diferencia es cuando lo paso como un parámetro, por ejemplo, entonces necesito hacer referencia a uno como Map y el otro como HashMap pero son de hecho el mismo tipo exacto de objeto? –
sepiroth
Sí, son exactamente el mismo objeto, se trata del * contrato * que está formando con cualquier código que lo use.Actualicé la respuesta un poco para aclarar. –
ah, entonces la diferencia es que, en general, Map tiene ciertos métodos asociados. pero existen diferentes formas de crear un mapa, como HashMap, y estas formas diferentes proporcionan métodos únicos que no tienen todos los mapas. Entonces, si utilizo un mapa, solo puedo usar los métodos Map, pero tengo un HashMap debajo para que se vean los beneficios de velocidad, los beneficios de búsqueda, etc. de HashMap en el mapa. Y si utilicé un HashMap, podría usar esos métodos específicos de HashMap y si finalmente necesito cambiar el tipo de mapa, es mucho más trabajo. – sepiroth