Там нет никакой разницы между объектами; у вас есть 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-> object, которые он разделяет (через методы доступа) с подклассами. Допустим, я пишу это с 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
контракт на изменение не повлиял бы на ее код.
Иногда ты не можешь этого сделать, иногда ты должен быть конкретным. Но если у вас нет причин быть ошибочными в сторону наименее конкретного интерфейса.