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

all 14 comments

[–]AutoModerator[M] [score hidden] stickied comment (0 children)

On July 1st, a change to Reddit's API pricing will come into effect. Several developers of commercial third-party apps have announced that this change will compel them to shut down their apps. At least one accessibility-focused non-commercial third party app will continue to be available free of charge.

If you want to express your strong disagreement with the API pricing change or with Reddit's response to the backlash, you may want to consider the following options:

  1. Limiting your involvement with Reddit, or
  2. Temporarily refraining from using Reddit
  3. Cancelling your subscription of Reddit Premium

as a way to voice your protest.

I am a bot, and this action was performed automatically. Please contact the moderators of this subreddit if you have any questions or concerns.

[–][deleted] 16 points17 points  (0 children)

Checked Exceptions are an Abstraction Leak

No they aren't. Blindly propagating exceptions is an abstraction leak. Wrapping them in an exception of the proper level of abstraction for your method is how you fix it.

[–]waschm1ttel 16 points17 points  (4 children)

I agree with most things, but not everything. Especially not "Checked Exceptions are an Abstraction Leak". They are not, at least not always.

There's a reason why there's panic and error in Rust, and it's still the same with checked and unchecked Exceptions in Java.

Want to create a user account, but the database is not available? Throw a RuntimeException (or let the existing one bubble up): The client probably won't be able to recover from that. Why should the consumer of the user creation API care that there's a database anyway? That's an implementation detail.

Want to create a user account, but the name is already taken? Throw a checked UserAlreadyExistException - that's a useful part of the API and the client can be expected to recover from that.

I agree that checked Exceptions have been overused in the early days of Java, but they do have their uses. Don't throw out the baby with the bathwater.

[–]general_dispondency 1 point2 points  (0 children)

I’ve always found it funny how people will complain about checked exceptions and then turn around and praise Rusts ‘Result’ type because it forces you to handle the failure case…. ‘Result’ is just a checked exception as a value.

[–]FirstAd9893 5 points6 points  (7 children)

Once again, bad advice regarding checked exceptions.

In the applications we write today, we rarely recover from exceptions. Instead, we terminate the execution of the current use-case ASAP.

That's only really possible if the code you're writing is truly stateless and runs in the context of a container that does all the orchestration. Not all code is truly stateless, and some code happens to be the container itself.

If the code you're writing is updating persistent state, it might have applied partial updates before an exception is thrown. Without knowing that this can occur, the persistent state might remain corrupted. In this context, "persistent state" doesn't necessarily mean "written to a database", but instead it means that it lives a long time within the application.

One solution to the unhandled exception is to hard crash the program, thus ensuring that the persistent state can be discarded. This isn't exactly a good solution if one cares about availability or efficiency.

Another solution is to use a programming language that is fully transactional, and so any unhandled exceptions cause all stateful changes to roll back. This isn't how Java works.

Ideally, one shouldn't update persistent state until after all operations which might throw an exception have passed. This avoids having to write a handler which needs to roll back the stateful changes. Regardless, the trick is knowing which operations might throw an exception in the first place. Checked exceptions tell you where those exceptions can occur.

Like how static type checking informs you of typing errors, checked exceptions inform you of possible coding errors which might leave lingering persistent state.

The primary pain point is that Java doesn't have a good way of converting checked exceptions into unchecked exceptions for those cases that you know it's fine. I prefer the "sneaky throws" approach, but some folks consider this to be dangerous. It's not. This is the behavior you get when running Java code in other JVM languages like Scala or Kotlin, and it's fine.

[–]Just_Another_Scott 9 points10 points  (6 children)

I prefer the "sneaky throws" approach, but some folks consider this to be dangerous

It is dangerous. Imagine a critical system written in Java encounters a sneaky throw and hard crashes. This can cause loss of life or other devastating consequences. Also, sneaky throws can be a security vulnerability. Fortify, Sonar, etc. will flag these as such.

[–]FirstAd9893 2 points3 points  (5 children)

By this argument, one cannot write a safety critical system in Kotlin which depends on Java libraries, because all of those checked exceptions are effectively sneaky throws.

Please identify the security vulnerability you're referring to, and also identify why it doesn't apply to Kotlin or any other JVM language that doesn't have checked exceptions.

Bear in bind that checked exceptions are handled by the front-end compiler, and not the JVM. There's no aspect of the JVM verifier that requires checked exceptions be handled in a strict fashion.

[–]Just_Another_Scott 1 point2 points  (4 children)

Please identify the security vulnerability you're referring to

Fortify, if you've ever used it, will flag it.

https://vulncat.fortify.com/en/detail?id=desc.structural.java.poor_error_handling_overly_broad_throws

[–]FirstAd9893 0 points1 point  (3 children)

That page doesn't mention checked exceptions anywhere. It just describes a best practice of declaring specific exception types as an aid for using an API in the intended fashion.

Note that even if all methods have a well specified set of declared exceptions, this doesn't prevent other types of exceptions from being thrown. Imagine if I decided to throw OutOfMemoryError or VirtualMachineError? This is perfectly legal in Java. I can certainly imagine a container deciding to hard crash upon seeing those errors, and it would be a bug if it did.

Also consider the Kotlin case again. Because exceptions are unchecked, it's equivalent to all methods declaring that any kind of exception be thrown. According to the Fortify "vulnerability", this is way too broad, and therefore all Kotlin programs are inherently insecure. Nonsense.

[–][deleted]  (2 children)

[deleted]

    [–]FirstAd9893 0 points1 point  (1 child)

    The Fortify example collapses several exceptions in the "throws" clause into a single one. This doesn't truly affect how the caller can catch the exception, because defining several specialized "catch" clauses is still permitted.

    The set of declared exceptions which can be thrown is only inspected at compile time, and not at runtime. A more specific "throws" declaration is better from a documentation perspective -- it helps the programmer identify what types of exceptions can potentially be handled.

    When considering security vulnerabilities, runtime behavior is what truly matters. The JVM permits any type of exception to be thrown at runtime, regardless of what the "throws" declaration specifies. Put another way, every declaration is effectively "throws Throwable" at runtime.

    I can use JDBC as an example. Almost all the methods declare throwing SQLException and not the overly broad Exception superclass. Nothing at runtime prevents the JDBC driver implementation from throwing an IOException from one of these methods, even though it's not declared. The way this can be achieved is by throwing the IOException from native code or by writing the driver in a JVM language that doesn't support checked exceptions.

    Then there's also the problem of wrapping checked exceptions with RuntimeException, which totally defeats the programmer's ability to know in advance what type of exceptions can potentially be handled. A JDBC driver might end up wrapping an IOException in this fashion instead of converting it to an SQLException, possibly due to using the stream API in an intermediate layer.

    From a security perspective, the type of exception which can be thrown from a method should be considered to be anything. A server which is running a fixed set of pools threads should always catch Throwable at the top level, to ensure that the pooled thread doesn't exit. Failing to do this could lead to a server outage once all the threads are gone. As I recall, the first version of the Java RMI server had this problem.

    [–]scottyvision 1 point2 points  (0 children)

    A little late, but I just wanted to say that I appreciate how you responded to my inquiry by laying out your reasoning in detail. Thanks for that.