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