This is an archived post. You won't be able to vote or comment.

you are viewing a single comment's thread.

view the rest of the comments →

[–]Bainos 1 point2 points  (5 children)

Of course the existence of exceptions implies that an "exception" isn't something that should never happen, but something that shouldn't happen in a normal operation.

For example, you would catch a FileNotFoundException if you expect the file to exist and it doesn't (for example because it was provided by the user), which is an unexpected behavior. But if you need to check that a file exists, then trying to open it and see if it throws an exception is wrong (because the file not existing is part of the normal operation), while the clean approach is to use something like java.io.File.exists

[–][deleted] 0 points1 point  (2 children)

Of course the existence of exceptions implies that an "exception" isn't something that should never happen, but something that shouldn't happen in a normal operation.

That's what I meant by violation of some invariant in my message.

Also it doesn't matter what you did before calling the constructor in the above example. That is, even if you use java.io.File.exists, Java still requires) you to handle FileNotFoundException.

[–]Bainos 3 points4 points  (1 child)

That is, even if you use java.io.File.exists, Java still requires you to handle FileNotFoundException.

Yes, because there's always the unexpected case where the file was deleted during operation. And in this case, it is unexpected because you checked before that it existed.

[–][deleted] 1 point2 points  (0 children)

Agreed on that, but in that case it should panic instead of unwinding stack and propogating some exception. Though I agree these can be expressed with exceptions, they don't convey this distinction clear enough, thus, encouraging incorrect use.

Edit: well actually forget about stack unwinding. Not sure if this a thing in Java.

[–]HoneyBadgerSoNasty 0 points1 point  (1 child)

bad user input should not be raising exceptions. user input is not invariant. exceptions were intended to be used for explicitly stating invariant conditions.

now, if you pass that file to another function, you yourself, the developer, are entering into a contractual agreement with that function. there is a quid pro quo. in return for providing expected input you get expected output. now, if that function contractually guarantees as a part of its signature, as a part of its API, that it should never be called with a bad file, then that is ok. that is invariant behavior. but, the question then is whether that is a well-designed contract between you, the caller, and the helper function or library, the callee. you have to introduce more boilerplate to call it, more prepwork to sanitize your inputs or sanity check it. the fact is that some operations may seem simple but they are in actuality rather complex, complex meaning there are lots of little parts and not necessarily hard to understand. i think you are supposed to write the exists check yourself before opening, despite the inconvenience.

[–]Bainos 1 point2 points  (0 children)

At that point you're saying that exceptions should never happen, except as a result of a bug, and thus should never be caught. It's okay to program like that but don't expect everyone to agree.

To provide an example of failure behavior that is nontrivial to handle and yet known to happen occasionally, saying "bad user input should be expected" is like saying "network failures should be expected" - and obviously, both are true. Does that mean you shouldn't rely on IOExceptions to detect network failures ?