Nie ma różnicy między przedmiotami; masz HashMap<String, Object>w obu przypadkach. Istnieje różnica w interfejsie, jaki masz do obiektu. W pierwszym przypadku interfejs jest HashMap<String, Object>, podczas gdy w drugim jest Map<String, Object>. Ale podstawowy obiekt jest taki sam.
Zaletą użycia Map<String, Object>jest to, że możesz zmienić obiekt bazowy na inny rodzaj mapy bez zerwania umowy z dowolnym kodem, który go używa. Jeśli zadeklarujesz to jako HashMap<String, Object>, musisz zmienić umowę, jeśli chcesz zmienić implementację.
Przykład: Załóżmy, że piszę tę klasę:
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...
}
Klasa ma kilka wewnętrznych map ciągu typu string>, które dzieli (za pomocą metod akcesorów) z podklasami. Powiedzmy, że piszę od HashMappoczątku, ponieważ uważam, że jest to odpowiednia struktura do użycia podczas pisania klasy.
Później Mary pisze kod podklasujący go. Ma coś, co musi zrobić z obiema thingsrzeczami moreThings, więc naturalnie umieszcza to we wspólnej metodzie i używa tego samego typu, którego użyłam na getThings/ getMoreThingspodczas definiowania swojej metody:
class SpecialFoo extends Foo {
private void doSomething(HashMap<String, Object> t) {
// ...
}
public void whatever() {
this.doSomething(this.getThings());
this.doSomething(this.getMoreThings());
}
// ...more...
}
Później zdecydować, że właściwie, to lepiej, jeśli mogę użyć TreeMapzamiast HashMapw Foo. Aktualizuję Foo, zmieniam HashMapna TreeMap. Teraz SpecialFoojuż się nie kompiluje, bo złamałem kontrakt: Foozwykłem mawiać, że to HashMaps, ale teraz to zapewnia TreeMaps. Więc musimy to SpecialFooteraz naprawić (i tego rodzaju rzeczy mogą przeniknąć przez bazę kodu).
O ile nie miałem naprawdę dobrego powodu do dzielenia się tym, że moja implementacja używała HashMap(i tak się dzieje), to co powinienem był zrobić, to zadeklarować getThingsi getMoreThingspo prostu powrócić, Map<String, Object>nie będąc bardziej szczegółowym. W rzeczywistości, z wyjątkiem dobrego powodu, aby zrobić coś innego, nawet w środku Fooprawdopodobnie powinienem zadeklarować thingsi moreThingsjako Map, nie 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...
}
Zwróć uwagę, że teraz używam Map<String, Object>wszędzie, gdzie mogę, będąc konkretnym tylko podczas tworzenia rzeczywistych obiektów.
Gdybym to zrobił, Mary zrobiłaby to:
class SpecialFoo extends Foo {
private void doSomething(Map<String, Object> t) { // <== Changed
// ...
}
public void whatever() {
this.doSomething(this.getThings());
this.doSomething(this.getMoreThings());
}
}
... a zmiana Foonie SpecialFoozatrzymałaby kompilacji.
Interfejsy (i klasy podstawowe) pozwalają nam ujawniać tylko tyle, ile jest konieczne , zachowując naszą elastyczność pod przykryciem, aby wprowadzać odpowiednie zmiany. Ogólnie rzecz biorąc, chcemy, aby nasze referencje były jak najbardziej podstawowe. Jeśli nie musimy wiedzieć, że to jest HashMap, po prostu nazwij to Map.
Nie jest to reguła ślepa, ale ogólnie kodowanie do najbardziej ogólnego interfejsu będzie mniej kruche niż kodowanie do czegoś bardziej konkretnego. Gdybym to pamiętał, nie stworzyłbym Footakiego zestawu, w którym Mary byłaby porażka SpecialFoo. Gdyby Mary to zapamiętała, to mimo że popełniłem błąd Foo, zadeklarowałaby swoją prywatną metodę Mapzamiast, HashMapa moja zmiana Fooumowy nie wpłynęłaby na jej kod.
Czasami nie możesz tego zrobić, czasem musisz być konkretny. Ale jeśli nie masz ku temu powodu, popełniaj błąd w kierunku najmniej szczegółowego interfejsu.