Exception transparency - lone throws (no checked exceptions)
Alessio Stalla
alessiostalla at gmail.com
Fri Jun 11 00:54:32 PDT 2010
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