Please consider the com.google.common.collect.ImmutableMap
Programming to its interface, Map , creates a bug that could blow-up in the runtime as such:
Map<String, Integer> myMap = ImmutableMap.copyOf(justSomeMap);
myMap.put(key, val); // bang. runtime error.
In this case, the idea of “program to the interface, and don’t worry about the implementing class” introduces a possible runtime bug.
The author of the ImmutableMap factory method recognized that and made the return type a class, not an interface. The methods that mutate Map were deprecated in ImmutableMap thus allowing the compiler to issue warnings.
- Is everything I said above correct?
Next, a new interface ImmutableMapInterface could have easily been created for the ImmutableMap class. ImmutableMapInterface would exactly be Map but just without the methods that would allow ImmutableMap to mutate. Were there ImmutableMapInterface , you can keep programming to interfaces.
So, why is there no ImmutableMapInterface ? I am going to guess the reason is just expediency. I agree. Creating an interface for every single class sounds a little too extreme. right?
3
Java does this in a few occasions and it is an abomination unto man.
This sort of thing is a textbook violation of the Liskov Substitution Principle, which leads to the sort of bugs you describe. Well written code does not do that.
You are right that making an interface for every single class is absurd. You need to use a little pragmatism in deciding what to make an interface for and what not to. The other thing to remember is that advice existed back in the C, C++ days. “Program to an interface” does not mean a literal interface
in Java or C#. It works just as well with web apis or Internet protocols… Here an interface is a guaranteed/exposed behavior, not the underlying implementation.