> Let's say that the creator of the interface allows IOException, but the backend of my implementation is a database so I have SQLExceptions, same issue.
The creator of the interface should decide on a generic exception type:
public interface UniversalStorageInterface {
void store() throws StorageException;
}
Then, in the present, the FileStorage can define (and throw) a FileStorageException (inherits from StorageException), and the SqlStorage may define (and throw) a DatabaseStorageException (same).
In the future, where we might want to store everything on a (non-existing yet) Cerulean backend, we would then define (and throw) a CeruleanStorageException (again, an implementation of StorageException), and our basic Interface would not need to change. We would also have no need to recompile FileStorage or SqlStorage (or proprietary SteelBlue storage where we have no code).
So now whoever creates the interface needs to define an exception type per method, and whoever implements the interface needs to define a subclass of that per method and catch-and-wrap every exception their callee raises.
You better add some serious tooling built into the language to facilitate this, because from experience ain't no way anyone's going to bother with this if they have to handroll it, even with IDE codegen assisting.
And it still doesn't solve the issue of generic interfaces like streams.
It doesn't have to be a type per method, and if someone is "going to bother with this" depends on the application — I guess in many cases it's perfectly fine to write a happy code with a single catch where you present a sad-face emoji and "try again later" line; in other types of applications this just won't fly.
Whether you do it with exceptions hierarchy or error types or something else, it's pretty much the same thing: you either handle all the errors or not (or anything in between).
How do you solve that error complexity with any other approach and how is that different using any other syntax sugar?
The only thing I dislike about exceptions is that they are ignored by default allowing people to all too easily write code with no error checking.
Note that StorageException in the previous example could also simply be a BaseStorageException that all the methods throw, even if one of them is DiskFullError(BaseStorageException) or ObjectStorageLimitExceededError(BaseStorageException), and another is InvalidFilenameError(BaseStorageException).
The creator of the interface should decide on a generic exception type:
Then, in the present, the FileStorage can define (and throw) a FileStorageException (inherits from StorageException), and the SqlStorage may define (and throw) a DatabaseStorageException (same).In the future, where we might want to store everything on a (non-existing yet) Cerulean backend, we would then define (and throw) a CeruleanStorageException (again, an implementation of StorageException), and our basic Interface would not need to change. We would also have no need to recompile FileStorage or SqlStorage (or proprietary SteelBlue storage where we have no code).