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