9
votes
\$\begingroup\$

I recently had a discussion in the forum of an API, because they changed an exception from checked to unchecked. I believed it needs to be checked, because it is recoverable. The arguments of "the other side" were verbosity and tediousness of try/catch or throws.

If it were purely theoretical question, I'd be right, but I agree that in practice it is sometimes tedious to write all these try/catches just for the sake of rethrowing unchecked exceptions, or logging.

So, an idea came to my mind, and I wonder whether it's viable. I'll illustrate with simple code:

public interface Foo {
    String foo() throws Exception;
}

public interface EasyFoo extends Foo {
    String foo();
}

These are two interfaces that define the same method (and this is enforced by inheritance), but the "easy" version does not define throwing checked exceptions. Then come 2 default implementations:

public class FooImpl implements Foo {

    @Override
    public String foo() throws Exception {
        return "foo";
    }
}

public class EasyFooImpl implements EasyFoo {

    Foo foo;

    public EasyFooImpl(Foo foo) {
        this.foo = foo;
    }

    @Override
    public String foo() {
        try {
            return foo.foo();
        } catch (Exception ex) {
            throw new RuntimeException(ex);
        }
    }
}

The latter delegates to the former, wrapping all exceptions in runtime exceptions.

And finally a factory:

public class FooFactory {
    public static Foo createFoo() {
        return new FooImpl();
    }

    public static EasyFoo createEasyFoo() {
        return new EasyFooImpl(new FooImpl());
    }
}

The benefits:

  • the user of the API can choose how he likes to use the implementation. If he doesn't intend to do anything with the checked exceptions, he can use the "easy" version
  • you support only one interface. The 2nd is the same, and you'll just have to add the methods that you have in the main one.
  • the user can use the EasyFoo in places where Foo is required:

    EasyFoo foo = FooFactory.createEasyFoo();
    helper.doSomething(foo); // which is public void doSomething(Foo foo);
    

Enough foos - the question is, is this a viable solution that can reduce verbosity while retaining the good sides of checked exceptions?

\$\endgroup\$

locked by Jamal May 31 '14 at 3:54

This question exists because it has historical significance, but it is not considered a good, on-topic question for this site so please do not use it as evidence that you can ask similar questions here. This question and its answers are frozen and cannot be changed. See the help center for guidance on writing a good question.

Read more about locked posts here.

migrated from stackoverflow.com Feb 8 '11 at 20:25

This question came from our site for professional and enthusiast programmers.

  • 1
    \$\begingroup\$ if you really want to recover from an unchecked exception, you can still explicitly catch it right? \$\endgroup\$ – hvgotcodes Feb 8 '11 at 17:43
  • 1
    \$\begingroup\$ @hvgotcodes you can, but the API becomes less evident. And by default users will start coding without even knowing exceptions can arise there. They'll be lucky if the exception happens soon, but it might happen in production. And as I noted - unchecked exceptions are not meant to be recovered from. \$\endgroup\$ – Bozho Feb 8 '11 at 17:45
  • \$\begingroup\$ @bozho, right -- Im just trying to decide if its worth the work to stand up 2 implementations for the same functionality. If you were to do it, your approach seems reasonable... \$\endgroup\$ – hvgotcodes Feb 8 '11 at 17:46
  • \$\begingroup\$ incidentally, is this hibernate? \$\endgroup\$ – hvgotcodes Feb 8 '11 at 17:47
  • 2
    \$\begingroup\$ just because the exception can be recovered from, will most people want to recover? You Pattern actually looks pretty good -- you could do some config magic so the developer could basically say 'easy' or 'full' so that way they don't need to do anything to go one way or the other... \$\endgroup\$ – hvgotcodes Feb 8 '11 at 17:51
4
votes
\$\begingroup\$

Just accept the fact that the exception is unchecked and move on.

The industry is moving away from checked exceptions: C++ never had them, C# decided not to follow Java in this place, and the latest frameworks for Java are using unchecked exceptions.

Face it, checked exceptions are a nice idea that doesn't work in practice.

As a result, the thing you try in your code, will never pass any code review I'll be part of.

\$\endgroup\$
  • 2
    \$\begingroup\$ luckily, I'm not doing the above, I'm just having an idea. ;) as of why checked exceptions don't work in practice - because they were misused too much. They have to exist, but need to be used rarely. \$\endgroup\$ – Bozho Feb 9 '11 at 7:29
  • \$\begingroup\$ @Bozho Checked exceptions do not work because they do not scale to RL sized projects. \$\endgroup\$ – Sjoerd Feb 9 '11 at 15:06
  • \$\begingroup\$ Can you stop the industry coercing the super first statement in constructor in the same way? \$\endgroup\$ – Val Dec 14 '13 at 20:58
1
vote
\$\begingroup\$

Ok, I'm going to assume that this is a honest-to-goodness exceptional case, that is extremely unlikely to happen. It isn't like java.sql, that throws an exception when opening a connection fails, which can happen very easily and should be handled by returning null.

If there are people who want this exception unchecked, they should write their own wrapper for it, IMO. If you did want it in the library, though, I'd do it exactly as you did for interfaces. The unchecked version doesn't depend on the checked vesion's implementation, so you only ever need one wrapper EasyFooImpl.

I would wonder about the design of the library if I ever saw that. Checked or unchecked, it's best to avoid throwing exceptions altogether. There are only two valid reasons I see as being "exception-worthy":

  • Where an unknown operation leaves the code in a unknown/invalid state. This should unrecoverable by the code that throws the exception.
  • Where it is impossible to return an invalid state, such as null or 0. An example that comes to mind is Integer.parse(). You can't return null; it requires int. And you can't return 0 or NAN because that is in the valid return set of the method. An exception the cleanest, most readable way you can signal that the string is unparsable to an integer.
\$\endgroup\$
  • 1
    \$\begingroup\$ well, in this case the failure can be due to network problem. So I can retry, or decide return empty result, or to propagate above \$\endgroup\$ – Bozho Feb 8 '11 at 21:37
  • 1
    \$\begingroup\$ The fact that you do not like exceptions, does not make them worthless. Exceptions are useful, especially unchecked ones. \$\endgroup\$ – Sjoerd Feb 9 '11 at 0:47
  • \$\begingroup\$ It's not that I don't like exceptions; I think that they are very useful. They are just overused in many cases. However, checked exceptions force the exceptional case to be handled close to the point it was thrown. In most cases, that code has the best context to handle the problem, and it can swallow it if the problem is not a show-stopper. \$\endgroup\$ – Michael K Feb 9 '11 at 13:39
  • \$\begingroup\$ I feel that in a framework/library, all exceptions should be checked. If the framework can't handle the problem, then it's probably important. The calling code should not be able to ignore the problem without acknowledging that it exists. To me, making the exception unchecked is tantamount to saying that it doesn't matter. \$\endgroup\$ – Michael K Feb 9 '11 at 13:45
  • 1
    \$\begingroup\$ Usually, the calling code does not care which exception has occured, as it cannot handle it anyway. Just let it bubble up to the main loop, which can show a dialog to the user, generate an error htlm page, or log it to a log file, depending on the type of application. The direct caller does not know the type of application, so cannot select the correct action. \$\endgroup\$ – Sjoerd Feb 9 '11 at 15:11
1
vote
\$\begingroup\$

Just to add in conversation:

IMHO Checked Exception are included for a reason. A method with a reasonable chance of failure should throw a checked Exception. e.g. IO classes rely on IO Devices which might not available at the time and thus throw a checked exception, IOException. The benefit of declaring IOException as checked is that Java enforces providing error handling or alternative solution. This is not enforced in the case of exceptions derived from RuntimeException. IMHO this enforcement encourages coders to write robust code which is better then not having an extra throws clause.

\$\endgroup\$
  • \$\begingroup\$ The trouble with checked exceptions is that the language/framework is trying to guess whether or not you should handle an exception. But the guesses aren't perfect and in some cases it will make me catch the exception when I can't do anything about it, and it won't make me catch the exception when I should. Furthermore, my experience suggests that instead of writing robust code, java coders too often catch and throw away the exceptions. \$\endgroup\$ – Winston Ewert Feb 13 '11 at 1:35
  • 2
    \$\begingroup\$ If Java would only force me to handle a checked exception, I would be happy. I would a catch-all try-catch in the main loop, and report an error in an appropiate way. However, Java does not allow me to do that. In fact, Java forces me to handle a checked exception in the direct calling code. The trouble is the direct in that statement. \$\endgroup\$ – Sjoerd Feb 13 '11 at 23:40
  • \$\begingroup\$ @Sjoerd: If a method throws a checked exception in a situation where the direct calling code doesn't expect it, even code further up the call stack is expecting that particular exception might be thrown, it's probably not expecting the situation that actually exists; code expecting the checked exception probably shouldn't receive one. \$\endgroup\$ – supercat Jan 16 '14 at 20:26
0
votes
\$\begingroup\$

If I had to do anything like that I'd just use java.lang.reflect.Proxy and generate the wrappers on the fly. You an check if the methods of Foo and EasyFoo are matching properly and disallow the EasyFoo impl (both interfaces should have the same method signatures but EasyFoo, no declared exceptions). In the end you have a reusable library to do dirty stuff. The invocation through the proxy will be slower but who cares.

So basically you need one class the generates proxies and one designated exception for the wrapping. You should wrap exceptions that are declared only.

Overall the code is synthetic sugar to me, though. You can always live w/ throws declaration instead. Even possible, I guess altering the compiler is not very feasible but the runtime verifier doesn't care of the exception declarations.

\$\endgroup\$
0
votes
\$\begingroup\$

Best practice in exception-handling is to "throw early, catch late".

Exceptions should mostly be handled at the business/ or request level -- it makes absolute sense to reconnect to an external system/ or resend a failed request. It generally does not make sense to retry a single byte, or a single SQL query.

Given that, the concept of checked exceptions -- forcing you to "handle immediately" or declare -- is of limited use.

Originally they were intended for contingencies, as opposed to failures. For example, an InsufficientFundsException when making a payment. Their spread to instead be used for all kinds of unrecoverable low-level systemic failures was largely a mistake.

You should design API insteads to use runtime exceptions, and (if you need to catch) should rethrow checked as runtime exceptions. In my projects, I declare AppSqlException, AppInternalException, AppConfigException etc which work very well.

See:

\$\endgroup\$

Not the answer you're looking for? Browse other questions tagged or ask your own question.