Exception transparency - lone throws (no checked exceptions)
Reinier Zwitserloot
reinier at zwitserloot.com
Wed Jun 16 09:48:10 PDT 2010
I make an observation that offering a way to opt out of checked exceptions
(project lombok's @Cleanup) has resulted in plenty of fairly rabid
opposition and in return I get flippant remarks and words put into my mouth.
Your observations, Alessio, are mostly correct, though lets not get ahead of
ourselves and embrace "lone throws" as some sort of bastion of type safety.
In the end, saying "I can throw anything" is barely an improvement over "I
don't declare anything specific". It IS safer, but only marginally so; just
think about the practicalities of it. What do you even do when one of the
methods you call includes a lone throws? You suggest you generate a warning
in the IDE to hint at the fact that you don't have a catch-everything clause
someplace, but obviously that'd be a horrible plan! *EVERY* closure
signature by default includes it in the "lone throws" proposal, which means
one must safeguard every single closure invoke with a try/catch(Exception)
OR with a @SuppressWarnings(), and that would be silly.
--Reinier Zwitserloot
On Fri, Jun 11, 2010 at 8:54 AM, Alessio Stalla <alessiostalla at gmail.com>wrote:
> On Fri, Jun 11, 2010 at 3:39 AM, Reinier Zwitserloot
> <reinier at zwitserloot.com> wrote:
> > Where did I claim it removes the ability to _use_ checked exceptions?
> > Offering a way to opt out of them is more than enough to irk some wraths.
>
> Java already offers such a way, wrapping with RuntimeException. Should
> we ban unchecked exceptions altogether, then?
>
> I must be honest: I think checked exceptions are one of Java's weakest
> points. However, Java is like it is and no one here is proposing to
> revolutionize it and drop checked exceptions. Actually in fact lone
> throws are safer than wrapping in runtime exceptions, because with
> lone throws tools have a way of statically checking if exceptions
> could sneakily be thrown out of a method. IDEs could show meaningful
> warnings - "foo() can throw anything but you only catch IOException,
> are you sure?" or "bar() throws but you don't catch anything nor
> declare your method throws" - which they can't show now when runtime
> exception wrappers are used.
>
> > Updating SAM classes is obviously a big problem. There's are a few
> bajillion
> > SAM classes out there in the greater java community, and oracle can only
> > update those in rt.jar. With the update to the SAM class, there's also a
> > semantic difference, which may be important. I mentioned it in my more
> > detailed proposal (folks presuming that if a block containing X
> statements
> > throws checked exception of type E, that only the method calls to methods
> > declaring throws E could possibly be the source of that exception, which
> > ceases to be true in the face of lone throws) - but it's much bigger
> issue
> > when lone throws is legal anywhere.
>
> How does this differ from the strawman proposal? If you encode generic
> exception information in the lambda's signature then the SAM class
> will need to add the same (or compatible) information to its single
> method signature. Note also that users are not forced to update all
> SAM classes, just the ones that they want to be targets of
> closure-to-SAM conversion for closures that can throw exceptions that
> the existing SAM does not throw. A case which definitely must be
> decided by human intervention, since it's not always the right thing
> to do.
>
> Alessio Stalla
>
> > On Thu, Jun 10, 2010 at 11:34 PM, Alessio Stalla <
> alessiostalla at gmail.com>
> > wrote:
> >>
> >> 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