Різниці між об’єктами немає; у вас є HashMap<String, Object>
в обох випадках. Існує різниця в інтерфейсі, який ви маєте до об'єкта. У першому випадку інтерфейс є HashMap<String, Object>
, тоді як у другому - це Map<String, Object>
. Але базовий об’єкт той самий.
Перевага використання Map<String, Object>
полягає в тому, що ви можете змінити базовий об'єкт на інший вид карти, не порушуючи контракт з будь-яким кодом, який ним використовується. Якщо ви визнаєте це таким HashMap<String, Object>
, вам доведеться змінити договір, якщо ви хочете змінити базову реалізацію.
Приклад: Скажімо, я пишу цей клас:
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...
}
Клас має пару внутрішніх карт об’єкта string->, яким він ділиться (за допомогою методу accessor) з підкласами. Скажімо, я пишу це з HashMap
s для початку, тому що я думаю, що це відповідна структура, яку слід використовувати при написанні класу.
Пізніше Мері пише код, підкласуючи його. У неї є щось, що їй потрібно зробити з обома, things
і moreThings
, природно, вона вважає, що це звичайний метод, і вона використовує той самий тип, який я використовував getThings
/ getMoreThings
при визначенні свого методу:
class SpecialFoo extends Foo {
private void doSomething(HashMap<String, Object> t) {
// ...
}
public void whatever() {
this.doSomething(this.getThings());
this.doSomething(this.getMoreThings());
}
// ...more...
}
Пізніше, я вирішив , що на насправді, це краще , якщо я використовую TreeMap
замість HashMap
в Foo
. Я оновлюю Foo
, змінюючи HashMap
на TreeMap
. Тепер, SpecialFoo
більше не компілюється, тому що я порушив контракт: Foo
раніше говорив, що це передбачено HashMap
s, але тепер він надає TreeMaps
натомість Тому нам доведеться виправити SpecialFoo
зараз (і така річ може прошиватись через кодову базу).
Якщо у мене не було дійсно вагомих причин поділитися тим, що моя реалізація використовує HashMap
(і це трапляється), те, що я повинен був зробити, це оголосити getThings
і getMoreThings
як тільки повернутися, Map<String, Object>
не будучи більш конкретним, ніж це. Насправді, заборона вагомої причини зробити щось інше, навіть усередині, Foo
мабуть, я повинен заявити things
і moreThings
як Map
, ні 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...
}
Зауважте, як я зараз використовую Map<String, Object>
всюди, де я можу, лише буду конкретним, коли створюю фактичні об'єкти.
Якби я зробив це, то Марія зробила б це:
class SpecialFoo extends Foo {
private void doSomething(Map<String, Object> t) { // <== Changed
// ...
}
public void whatever() {
this.doSomething(this.getThings());
this.doSomething(this.getMoreThings());
}
}
... а зміна Foo
не SpecialFoo
зупинила б збирання.
Інтерфейси (та базові класи) дозволяють нам розкрити лише стільки, скільки необхідно , зберігаючи нашу гнучкість під обкладинкою, щоб внести зміни в міру необхідності. Загалом, ми хочемо, щоб наші посилання були максимально базовими. Якщо нам не потрібно знати, що це HashMap
, просто назвіть його Map
.
Це не сліпе правило, але загалом кодування до самого загального інтерфейсу буде менш крихким, ніж кодування до чогось більш конкретного. Якби я пам’ятав це, я б не створив цю програму, Foo
яку Мері створила на провал SpecialFoo
. Якби Мері запам'ятала це, то, хоч я і заплутався Foo
, вона заявила б про свій приватний метод Map
замість цього, HashMap
і мій Foo
контракт на зміну не вплинув би на її код.
Іноді ти не можеш цього зробити, іноді ти маєш бути конкретним. Але якщо у вас немає причин, помиліться на найменш специфічному інтерфейсі.