11 ms·
I am surprised an even more obvious interface definition of public interface ExampleInterface { void foo() throws IOException, AnotherException; } w
by necovek 3y ago
I am surprised an even more obvious interface definition of
public interface ExampleInterface {
void foo() throws IOException, AnotherException;
}
was not covered: is this also not supported?
That would solve a lot of use cases if inheritance is respected.
- ailef 3y agoThis is indeed the supported way to do it.
- necovek 3y agoThanks for confirming as I am not familiar with Java, but good to know it does make sense. As such, I am not sure what did the original author complain about because that's perfectly sufficient language support.
- masklinn 3y agoIt is supported, but does not actually solve anything, you're still limited to whatever checked exceptions the creator of the interface has decided might be valid (it's just that in the original it's "none"). 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. Is the creator of the interface supposed to add every checked exception in the standard library? Ignoring that this still isn't all of them, then the caller is hosed because they have to handle (or rethrow) every single one of them which is no better. So they probably go "fuck it" and just handle / rethrow Exception. At which point you can just do that on the interface, and it's not helping anyone, and you're better off just not saying anything. Checked exceptions simply don't mesh with the language: because the language provides limited to no way to abstract over them they're an issue every time you're trying to be generic over anything, the only situation in which they kinda sorta work is if the entire callchain is concrete, which not only is very limiting but it's very much not idiomatic.
- throwaway290 3y agoMaybe your SQLException should be caller's IOException? I don't code Java but IMO callers should be hit with errors at the right abstraction level. If caller is using that interface and not your low-level backend functions directly then caller should expect IOException not SQLException. If the interface does not provide exception types for all cases so you are stuck rethrowing your error as a wrong one, and I'd say that means the interface is bad but not checked errors are bad...
- funnymony 3y ago“bad” might not be the right word. Maybe better “usefulness for current population of developers is very limited”.
- maratc 3y ago> 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).
- masklinn 3y agoSo 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.
- PretzelPirate 3y agoWouldn't it still be a problem? By putting "throws [Exception List]" in the interface, the interface making assumptions about the implementation details of every potential implementation. The interface can't know every exception that an implementation might throw, and using your example, some implementations may not use IP at all and won't throw those exceptions. It seems like you'd end up needing to list every possible exception on "foo() " when defining the interface, and then handle every possible exception in any code that uses "ExampleInterface". At that point, it would be better to not annotate exception information at all.
- necovek 3y agoAs others have pointed out, my example would have been better if it said `ExampleExceptionFoo, ExampleExceptionBar` to more clearly indicate that an interface would define what exceptions it could throw as part of the contract. Individual implementations could extend those base exceptions if they need to. There is not much difference between this and using error types, except the syntax sugar.
- thewix 3y agoExceptions should absolutely be a part of an interface and it stops you from leaking implementation details. If I have an IRepository interface then none of the methods should ever throw SqlException, HttpException, or anything like that. You'd map them to something else. Exceptions in most languages are implicitly part of the contract, and not having consistent ones on an interface breaks Liskov's Substitution Principle.
- grumbel 3y ago> the interface making assumptions about the implementation details of every potential implementation. No, as you'd force the implementation to catch their implementation specific exceptions and rethrow them in the way defined by the interface. That's really the crux of it, when I call foo(), I expect to see a FooException when something goes wrong, since that's the only one I can meaningful react to. If I get an ImplementationDetailException that I never heard from and that isn't specified in the interface, how am I supposed to react to that in a meaningful way? If exception are supposed to be used for error handling, you have to actually report and handle them in a well specified manner, you can't just treat them as a slightly less crashy version of a SIGSEGV.