4 ms·
Checked exceptions fundamentally violate most of the principles of object-oriented programming by forcing you to expose implementation details into the method t
by wvenable 8y ago
Checked exceptions fundamentally violate most of the principles of object-oriented programming by forcing you to expose implementation details into the method type signature. Encapsulation, abstraction, and even polymorphism are violated by this.
Java programmers typically get around this by creating ClassNameException classes and then stuff the real exception (untyped) into the innerException property.
Checked exceptions are conceptually and fundamentally flawed.
- dragonwriter 8y ago> Checked exceptions fundamentally violate most of the principles of object-oriented programming by forcing you to expose implementation details into the method type signature No, they don't. They can be abused in a way which does this (and can be implemented in a way which unnecessarily creates avenues for it), but checked exceptions in general are isomorphic to static return types, and don't violate OOP any more than static return types do.
- wvenable 8y agoStatic return types would violated OOP as well if they exposed implementation details unrelated to the result of the operation. If you have a method called GetPrice() that uses a database, you know it, because you have to expose all the possible exceptions. If you change that to use a file or a network service, you have to change every caller and every caller of that. Is that abstraction? No. Is it polymorphism, also no. You can't have two different implementations of GetPrice() that are compatible unless they have exactly the same exception result. What's the value of that?
- dragonwriter 8y ago> Static return types would violated OOP as well if they exposed implementation details unrelated to the result of the operation. Sure, but neither checked exceptions nor static return types inherently do that. Bad practices around either can, and bad language design around either can make that more likely. (I honestly think unchecked exceptions are a bigger problem, because they make poor chocies less obvious.) > If you have a method called GetPrice() that uses a database, you know it, because you have to expose all the possible exceptions. Only if you are letting them bubbled up as is; you only have to expose exceptions you don't handle, which is equivalent to if you shouldn't expose an exception to a caller, you must handle it, perhaps with a handler which simply throws an exception that is appropriate to expose.
- wvenable 8y ago> you only have to expose exceptions you don't handle Which is the vast majority of exceptions because by-definition exceptional conditions can't be handled within the method. That's why they are exceptions. The presumption that most exceptions are handled locally is wrong. Exceptions, if they are handled at all, are almost always handled far away from the site they were thrown. Further it doesn't even matter which method it is! I can handle all network errors by restarting the operation from the start; I don't need to know which of the 3,000 possible methods up from my loop actually triggered the network exception. If any of those methods change from throwing to not throwing a network error, it literally doesn't matter. But I do need to know it's a network error and not some abstracted LibraryNameException.
- apta 8y ago> But I do need to know it's a network error and not some abstracted LibraryNameException. How is that any different from using return values like Rust does? It seems that libraries have their own `LibraryError` type which you have to decode anyway.
- dnomad 8y ago> Static return types would violated OOP as well if they exposed implementation details unrelated to the result of the operation. You're arguing against a particular usage of checked exceptions. You don't have to use checked exceptions like that so the argument is not convincing. > If you have a method called GetPrice() that uses a database, you know it, because you have to expose all the possible exceptions. And if you had a method called getPrice() that should always fail if a product is part of a bundle and cannot be purchase separately then you would potentially want every caller to be aware of that possibility. One error condition is an implementation detail and the other is a fundamental invariant of the domain. Guess which one checked exceptions should be used for? > You can't have two different implementations of GetPrice() that are compatible unless they have exactly the same exception result. What's the value of that? That's the whole point. There are invariants that should be communicated in a type-safe manner. The confusion here isn't new and is actually well understood. All error signals fall into two buckets -- does my caller care and can she respond sensibly to it or will she just pass on the error and/or quit. The benefit of checked exceptions is maximum flexibility: you can guess (and it's always a guess) that your caller doesn't care and throw an unchecked exception or, if you feel really strongly that the caller should care because some key invariant has been broken, you can throw a checked exception. It's not an exact science but this question -- does my caller care? -- goes to the very heart of encapsulation and abstraction.
- wvenable 8y agoI disagree. If you have an polymorphic getPrice() as part of an interface than you can't possibly list all the invariants as individual well-typed exceptions. That would assume perfect knowledge of all possible implementations of that method now and in the future. That's not OOP. In a non-polymorphic case of a bundled item, you'd simply not have a getPrice() method on the item at all. It might simply not implement the PricedItem interface, for example. There are plenty of ways of ensuring all invariants are met, in a type-safe way, without using exceptions. If it's possible to come to situation as normal correct operation (such as an item in bundle) then it's not exceptional.
- mike_hearn 8y agoPeople are often lazy and do that, but it's not fundamental. You can and should hide exceptions behind more abstract types if you're using layers of libraries, but that happens with error codes too - a curl error code meaning "server returned 500" might get turned into a different error code meaning "could not download data file", losing the exact nature of why it failed because the library doesn't want to expose curl's constants in its own API. The big win of Java-style exceptions in this situation is there's a common protocol for chaining, so you can make more abstract errors but without losing any detail about what the original cause was. I find this helps me almost every day.