Exception transparency - lone throws (no checked exceptions)
Alessio Stalla
alessiostalla at gmail.com
Thu Jun 10 14:34:15 PDT 2010
On Thu, Jun 10, 2010 at 8:31 PM, Reinier Zwitserloot
<reinier at zwitserloot.com> wrote:
> 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.
No, it's not. It doesn't forbid checked exceptions or change their
semantics. It gives the power to an API writer to allow you to ignore
checked exceptions coming from the API methods. API writers already
have this power today: they just need to explicitly wrap checked
exceptions in unchecked ones. Spring, for example, does this
extensively. "lone throws" simply make this option easier.
> 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.
Right, but it changes it in such a way that old code will continue to
work unchanged. If Runnable.run was changed to include throws; you
could still call it without catching anything, as you do today.
> 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.
Then update the SAM classes, what's the problem if the update is
backwards-compatible? Are you thinking about user classes here? In
that case, the user is responsible to update the classes if she wants
them to be compatible with closures that can throw exceptions. They
would need to be updated anyway if the closure declared to throw
checked exceptions while the SAM class didn't. Or am I missing
something?
Regards,
Alessio Stalla
More information about the lambda-dev
mailing list