6

From javaDocs on java.lang.Exception class:

Checked exceptions need to be declared in a method or constructor's throws clause if they can be thrown by the execution of the method or constructor and propagate outside the method or constructor boundary.

But consider this code:

package other;

public class CheckedExceptionHandling {

    private static <E extends Exception> void throwException() throws E {
        throw (E) new CheckedException2(); // unchecked cast warning
    }

    private static void setUncaughtExceptionHandler() {
        Thread.currentThread().setUncaughtExceptionHandler((t, e) -> {
            System.out.println("Unhandled exception: " + e.getClass()); // reports CheckedExceptionHandling$CheckedException2
        });
    }

    public static void main(String[] args) /* no checked exceptions declared! */ {
        setUncaughtExceptionHandler();
        try {
            CheckedExceptionHandling.<CheckedException1>throwException();
        } catch (CheckedException1 e) {
            System.out.println(e); // never gets here
        }
    }
    // checked exceptions:
    public static class CheckedException1 extends Exception {}
    public static class CheckedException2 extends Exception {}

}

It compiles with a warning and the run-time result is:

Unhandled exception: class other.CheckedExceptionHandling$CheckedException2

I expected a compile-time error unreported exception CheckedException2; must be caught or declared to be thrown or incompatible types: CheckedException2 cannot be converted to CheckedException1 or at least a ClassCastException at run time.
But the compiler allows a checked exception to be left unhandled, undeclared and propagate outside the method main to the uncaught exception handler.
Why? Am I missing something here?

Yuri
  • 1,695
  • 1
  • 13
  • 23
  • Plus one. Nice well-written question. Java exception handling is broken. Eventually you'll get used to that and chill out. – Bathsheba Oct 06 '16 at 13:01
  • 1
    There is nothing broken here. It is all according to spec. – Tunaki Oct 06 '16 at 13:01
  • 2
    This looks a lot like the "sneaky throw". Search for the term on your search engine of choice, e.g. https://www.reddit.com/r/programming/comments/2x41h4/sneaky_exceptions_in_java/ – Andy Turner Oct 06 '16 at 13:01
  • 1
    The compiler clearly warned you that you are doing an _unchecked cast_. What is broken is not Java's exception handling, but your program. Just kidding, Java's checked exceptions are a giant ball of mud. – Marko Topolnik Oct 06 '16 at 13:02
  • 3
    See also http://stackoverflow.com/questions/14038649/java-sneakythrow-of-exceptions-type-erasure – Tunaki Oct 06 '16 at 13:06
  • 2
    @Yuri You're casting your `CheckedException2` to a `CheckedException1` , your `throwException()` says it throws a `CheckedException1` and your code also catches a `CheckedException1` - so that wouldn't be a compiletime error . However the cast `(E) new CheckedException2()` where your E is `CheckedException1` has some severe implications when using generics, described in the link Tunaki provided – nos Oct 06 '16 at 13:13

1 Answers1

0

The problem is that the compiler does not look past method bounds. Within throwsException(), there is no info about E other than "extends Exception", so the Compiler can not proof that the cast is wrong and gives a warning, not an error (as Marko already pointed out in the comments).

Within main(), the compiler sees only the signature of "throwsException", which is defined as "throws E" and in main(), E is CheckedException. As CheckedException is catched, everything is fine here.

Then, Java throws away all generic type info at compile time, so the cast to E is not present in the byte code. Thereby, the runtime can not detect the type error either.

What is broken here is not the exception handling, but the type system.

Alex
  • 871
  • 7
  • 23