Non c'è differenza tra gli oggetti; hai un HashMap<String, Object>
in entrambi i casi. C'è una differenza nell'interfaccia che hai con l'oggetto. Nel primo caso, l'interfaccia è HashMap<String, Object>
, mentre nel secondo è Map<String, Object>
. Ma l'oggetto sottostante è lo stesso.
Il vantaggio dell'utilizzo Map<String, Object>
è che puoi modificare l'oggetto sottostante in un diverso tipo di mappa senza rompere il contratto con qualsiasi codice che lo utilizza. Se lo dichiari come HashMap<String, Object>
, devi modificare il contratto se desideri cambiare l'implementazione sottostante.
Esempio: diciamo che scrivo questa 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...
}
La classe ha un paio di mappe interne di string-> object che condivide (tramite metodi accessor) con sottoclassi. Diciamo che lo scrivo con HashMap
s all'inizio perché penso che sia la struttura appropriata da usare quando si scrive la classe.
Più tardi, Mary scrive il codice subclassandolo. Ha qualcosa che deve fare con entrambi things
e moreThings
, quindi, naturalmente, lo mette in un metodo comune e usa lo stesso tipo che ho usato su getThings
/ getMoreThings
durante la definizione del suo metodo:
class SpecialFoo extends Foo {
private void doSomething(HashMap<String, Object> t) {
// ...
}
public void whatever() {
this.doSomething(this.getThings());
this.doSomething(this.getMoreThings());
}
// ...more...
}
Più tardi, decido che in realtà, è meglio se uso TreeMap
invece di HashMap
in Foo
. Aggiornamento Foo
, cambiando HashMap
in TreeMap
. Ora, SpecialFoo
non compilare più, perché ho rotto il contratto: era Foo
solito dire che forniva HashMap
s, ma ora TreeMaps
invece fornisce . Quindi ora dobbiamo risolvere SpecialFoo
(e questo genere di cose può incresparsi in una base di codice).
A meno che non avessi una buona ragione per condividere che la mia implementazione stava usando un HashMap
(e ciò accade), ciò che avrei dovuto fare era dichiarare getThings
e getMoreThings
semplicemente tornare Map<String, Object>
senza essere più specifico di così. In effetti, escludendo una buona ragione per fare qualcos'altro, anche all'interno Foo
dovrei probabilmente dichiarare things
e moreThings
come Map
, non 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 come sto usando Map<String, Object>
tutto il possibile, essendo specifico solo quando creo gli oggetti reali.
Se l'avessi fatto, allora Mary avrebbe fatto questo:
class SpecialFoo extends Foo {
private void doSomething(Map<String, Object> t) { // <== Changed
// ...
}
public void whatever() {
this.doSomething(this.getThings());
this.doSomething(this.getMoreThings());
}
}
... e il cambiamento Foo
non avrebbe SpecialFoo
impedito la compilazione.
Le interfacce (e le classi di base) ci rivelano solo quanto è necessario , mantenendo la nostra flessibilità sotto le coperte per apportare le modifiche appropriate. In generale, vogliamo che i nostri riferimenti siano il più basilari possibile. Se non abbiamo bisogno di sapere che è un HashMap
, chiamalo semplicemente un Map
.
Questa non è una regola cieca, ma in generale, la codifica per l'interfaccia più generale sarà meno fragile della codifica per qualcosa di più specifico. Se me lo fossi ricordato, non avrei creato un Foo
Mary con cui Mary avrebbe fallito SpecialFoo
. Se Mary lo avesse ricordato, allora anche se avessi sbagliato Foo
, avrebbe dichiarato il suo metodo privato Map
invece di HashMap
e il mio Foo
contratto di modifica non avrebbe avuto alcun impatto sul suo codice.
A volte non puoi farlo, a volte devi essere specifico. Ma a meno che tu non abbia un motivo per esserlo, vai verso l'interfaccia meno specifica.