I answered a question regaurding an
ImmutableMap. I suggested using the Proxy pattern.
The problem with this is that
Map contains a
put method, which would throw an
UnsupportedOperationException. Replacing other instances of
ImmutableMap would break the Liskov Subsitution principle. Not only that, the need to declare
putAll [violates the interface segregation principle]
Technically, there is no way to replace a
Map instance with
Map is simply an interface. So my question is:
Would creating an
ImmutableMap using the
Map interface be considered breaking the LSP, since
Map contains a
putAll method? Would not implementing
Map be considered an "Alternative Classes with Different Interfaces" code smell? How would one create an
ImmutableMap that abides by the LSP yet doesn't contain code smells?
Best How To :
In my view, an
ImmutableMap should implement
Map. It would be a bad idea not to implement
Map as there are many methods that accept a
Map as an argument and only use it in a read-only sense. I don't believe this does violate the Liskov Subsitution principle because the contract for
Map makes it clear that
put is an optional operation.
It is not ideal that classes implementing
Map have to implement
put, but the alternative would have been to have a complex hierarchy of interfaces each only including a subset of the possible optional methods. If there are
n optional methods, there would have to be
2^n interfaces to cover all the combinations. I don't know the value of
n, but there are some non-obvious optional operations, such as whether or not the
Iterator returned by
map.entrySet().iterator() supports the
setValue operation. If you combined this hierarchy with the hierarchy of interfaces and abstract classes that actually exists already (including
ConcurrentNavigableMap...) you would have a total mess.
So there is no perfect answer to this, but in my view the best solution is to make
Map and ensure that every method you write with a
Map as an argument clearly documents any properties the
Map must have and the exceptions thrown if the wrong type of
Map is passed.