Exception transparency - lone throws (no checked exceptions)
Reinier Zwitserloot
reinier at zwitserloot.com
Thu Jun 10 11:31:35 PDT 2010
SneakyThrows is possible in the following ways:
1. Use any non-java JVM language, such as scala, jython, or JRuby. None of
these languages have the concept 'checked exception', and neither does the
JVM ('throws' lists are of course in class files, but as far as the JVM is
concerned these are like annotations; the JVM does nothing with them other
than let you introspect the list). You can sneakythrow anything using this
tactic. In the end all you need to do is create a class file with a method
that does not have a 'throws' list but which does use the "ATHROW" opcode to
throw a checked exception. The class verifier nor the JVM runtime complains
about doing this; this is why jython, scala, etc all can compile just fine
without having the notion of checked exceptions.
2. Use MyClass.class.newInstance() - if MyClass's no-args constructor throws
a checked exception it will be sneakythrown onwards. The only two you can't
sneak with this are those checked exceptions that newInstance() itself
declares; InstantiationException and IllegalAccessException. This is the
reflection library oversight that Florian was talking about, I believe. It's
also the only one; both Method and Constructor's invoke/newInstance methods
solved this problem by wrapping exceptions into InvocationTargetException.
It's only Class.newInstance() that doesn't do this.
3. Use mixed class files. Compile "public class Sneaky { public static void
sneaky(Throwable t) { /* do nothing */ }}", then compile your code against
this class file, but at runtime, recompile *ONLY* Sneaky.class with this:
"public class Sneaky { public static void sneaky(Throwable t) throws
Throwable { throw t; }}". The JVM doesn't check if a method's signature's
addendums, such as the throws clauses, are still the same as they were when
you compiled against the API, so this works just fine. In practice you'd
have 2 sneakies, compile against the one, but use the other for runtime.
4. Generics:
public class Sneaky {
public static void sneaky(Throwable t) {
Sneaky.<RuntimeException>sneaky0(t);
}
@SuppressWarnings("unchecked")
private static <T extends Throwable> void sneaky0(Throwable t) throws T {
throw (T)t;
}
}
As far as I know these are the only ways, but its clear that one simply
cannot assume sneaky throws just don't happen; in particular assuming that
mixed class files or class files compiled by alternative languages never
happen is a bad idea.
Stephen: I wouldn't particularly mind your proposal, personally, but it's
effectively aborting the checked exception experiment. I'm okay with it
(heck, I tossed @SneakyThrows in lombok for a reason!) but from my
experiences with @SneakyThrows, there are lots and lots of java programmers
that have been ingrained with the notion that checked exceptions are what
keeps the world from imploding and will vehemently fight for them. Just a
friendly heads up :) There's an alternative implementation which is based on
explicitly stating the types you'd like to sneaky throw. I'm not quite sure
how one would mesh this with the closure syntax, but it has the advantage of
at least explicitly stating that, yes, I 'sneaky' throw IOException here.
Lombok's SneakyThrow works like this:
@SneakyThrows(IOException.class)
public void foo() { throw new IOException(); }
with just "@SneakyThrows" being a shorthand for
@SneakyThrows(Throwable.class). In regards to closures I believe your
proposal to be superior, if we go down this "let's give up on checked
exceptions" path.
However, your proposal has a flaw; I think it'll eventually boil down to
simply getting rid of the concept of checked exceptions in general. It
doesn't extend to SAMs, because in your system adding a "lone throws"
changes the signature. You can add a lone throws to an interface method
without breaking backwards compatibility as far as I can tell, but that's
not the problem. The problem is this: *Existing* SAM classes that aren't
updated to add a lone throws to their one abstract method CANNOT be created
by way of the closure syntax, because closures have implicit lone throws.
The compiler could perhaps be smart enough to realize the closure itself
doesn't actually throw any checked exception and convert the closure to a
SAM, taking care of a sizable chunk of use cases, but so far it looks like
SAM conversion is a job for the JVM and not the compiler so I'm not sure how
that would work.
--Reinier Zwitserloot
On Thu, Jun 10, 2010 at 6:26 PM, Florian Weimer <fweimer at bfk.de> wrote:
> * Stephen Colebourne:
>
> > Current checked exceptions are maintained, however users have the
> > choice to effectively turn them off. Given many APIs already do this
> > today (by wrapping checked exceptions in unchecked) this would
> > probably be used beyond just lambdas. (Checked exceptions remain a
> > religious issue for some - this change offers teams a choice as to how
> > they are handled.)
>
> It makes checked exception leaks official. IIRC, there is already one
> such leak (some reflection method not properly wrapping checked
> exceptions), but a general, by-design loophole seems a different
> matter.
>
> However, I have to admit that the simplicity of this proposal is
> rather tempting. It would lead to many simplifications in existing
> code, usually without sacrificing clarity. It should even be possible
> to retrofit existing APIs such as Runnable. In that regard, it's
> different from "throws Throwable".
>
> --
> Florian Weimer <fweimer at bfk.de>
> BFK edv-consulting GmbH http://www.bfk.de/
> Kriegsstraße 100 tel: +49-721-96201-1
> D-76133 Karlsruhe fax: +49-721-96201-99
>
>
More information about the lambda-dev
mailing list