There is an important difference between source compatibility and binary compatibility.
- If two versions of some class V1 and V2 are binary compatible, that means that classes compiled against V1 will run just fine against V2.
- If two versions of some class V1 and V2 are source compatible, that means that a class that could compile against V1 will compile just fine against V2.
Source compatibility does not automatically imply binary compatibility, as you've experienced.
When the source is compiled, the specific method signature to be called is decided by the compiler and stored in the .class
file (in this case it's doSomething(List)
).
If the class is changed and the method doSomething(List)
is removed while doSomething(Collection)
is added, then source compatibility is retained (because you can simply compile the same code against the new class), but binary compatibility is lost!
The Java Language Specification has an entire section on binary compatibility.
To summarize: while changing the argument type of a method to a more general type is (usually) source compatible, it is not binary compatible.
If you want to retain binary compatibility, then the change would have to look like this:
public void doSomething(Collection foo) { ... } // original method with changed argument type
public void doSomething(List foo) { // new binary compatibility method, just delegates to original one
doSomething((Collection) foo);
}