简体   繁体   中英

Java with Groovy handling of closures throwing Exceptions

We have a system that is possible to customise using groovy scripts and I've spotted a very strange effect on the type of exceptions throw from these scripts.

We have a groovy script with the following:

process {
   throw new Exception("weeee")
}

Process is defined as a Closure in the base class of the script:

public abstract class ScriptBaseClass extends Script {
   Closure process;

   public void process( Closure code ) {
      process = (Closure) code.clone();
   } 
}

In the Java class that actually runs the scripts we have the following method (omitted all the set up code as it doesn't seem relevant):

public void process() {
   try {
      script.process.call();
   } catch (Exception e) {
      logger.debug("exception thrown from groovy script", e);
      throw e;
   }
}

Note the process method here doesn't declare it throws any exceptions. However it quite clearly re-throws the Exception e that it caught. This code is valid, it compiles and runs quite happily. It throws the Exception as I wanted.

Does any one know how this is legal code? In theory I shouldn't be able to throw a checked exception out of a method that doesn't declare that it throws it.

It works because Java compiler (starting since Java 7) can determine re-thrown exception. For catch (Exception e) it thinks it's RuntimeException because there were no other (checked) exception declared in call() .

You can read about it there, for example: https://docs.oracle.com/javase/7/docs/technotes/guides/language/catch-multiple.html

So this code compiles perfectly:

public void xxxxx() {
    try {
        System.out.println('Hi!'); //or anything else w/o declared exceptions
    } catch (Exception e) {
        throw e;
    }
}

Java compiled sees that only RuntimeException could be caught here, so it doesn't ask you do declare anything.

But for this:

public void xxxxx() {
    try {
        throw new IOException(); //or anything that have declared checked exception
    } catch (Exception e) {
        throw e;
    }
}

it will fail to compile, because IOException could be caught and re-thrown

Groovy and the JVM don't really care about an exception being a checked one or not. Only the Java compiler cares about this here. In fact you can use the catch on any RuntimeException or its parent classes (of which Exception is one) without requiring it being declared as thrown from something called in the try-block. So it is fine by the compiler to catch Exception or Throwable even. Of course, from a program logic view it is an entirely different matter.

Introducing closures or anything Groovy related is unnecessary complexity. The following is valid Java code:

public class Demo {
    public void someMethod() {
        try {
            System.out.println("Hello");
        } catch (Exception e) {
            throw e;
        }
    }
}

Note that the following is not valid and will not compile:

import java.sql.*;

public class Demo {
    public void someMethod() {
        try {
            Connection c = DriverManager.getConnection("", "", "");
        } catch (SQLException e) {
            throw e;
        }
    }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM