【问题标题】:Java with Groovy handling of closures throwing Exceptions使用 Groovy 处理抛出异常的闭包的 Java
【发布时间】:2015-11-06 05:30:47
【问题描述】:

我们有一个可以使用 groovy 脚本进行自定义的系统,我发现这些脚本引发的异常类型会产生非常奇怪的影响。

我们有一个 groovy 脚本,其中包含以下内容:

process {
   throw new Exception("weeee")
}

进程在脚本的基类中被定义为闭包:

public abstract class ScriptBaseClass extends Script {
   Closure process;

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

在实际运行脚本的 Java 类 中,我们有以下方法(省略所有设置代码,因为它似乎不相关):

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

注意这里的 process 方法没有声明它抛出任何异常。然而,它很清楚地重新抛出了它捕获的异常 e。这段代码是有效的,它编译和运行非常愉快。它按照我的意愿抛出异常。

有谁知道这是法律法规吗?从理论上讲,我不应该能够从一个没有声明它抛出它的方法中抛出一个已检查的异常。

【问题讨论】:

  • 是的,这在 groovy 中是合法的。顺便说一句,在文档中找不到任何提及,但它一直在工作
  • @IgorArtamonov 实际上我知道这在 groovy 中是合法的,但是上面重新抛出异常的 process 方法在 Java 类中。通常如果你抛出一个检查异常,你必须声明它。这似乎是两种语言界面的一个奇怪案例。
  • 哦,抱歉,关于 groovy 的第一部分令人困惑,所以我错过了关于 java 的部分

标签: java exception groovy


【解决方案1】:

它之所以有效,是因为 Java 编译器(从 Java 7 开始)可以确定重新抛出的异常。对于catch (Exception e),它认为是RuntimeException,因为在call() 中没有声明其他(已检查)异常。

你可以在那里阅读它,例如:https://docs.oracle.com/javase/7/docs/technotes/guides/language/catch-multiple.html

所以这段代码编译完美:

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

Java 编译后发现此处只能捕获 RuntimeException,因此它不会要求您声明任何内容。

但是对于这个:

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

它将无法编译,因为IOException 可能会被捕获并重新抛出

【讨论】:

  • 很好的答案。在没有其他语言混入 JVM 的情况下,这是一件合理的事情(如果有点混乱)。然而,这确实意味着我们在与 groovy 代码交互时需要非常小心。
【解决方案2】:

引入闭包或任何与 Groovy 相关的东西都是不必要的复杂性。以下是有效的 Java 代码:

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

请注意,以下内容无效,不会编译:

import java.sql.*;

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

【讨论】:

    【解决方案3】:

    Groovy 和 JVM 并不真正关心异常是否被检查。这里只有 Java 编译器关心这个。实际上,您可以在任何 RuntimeException 或其父类(其中 Exception 是其中之一)上使用 catch,而无需将其声明为从 try 块中调用的东西中抛出。因此,编译器甚至可以捕获 Exception 或 Throwable。当然,从程序逻辑的角度看,完全是另外一回事。

    【讨论】:

    • 我知道 groovy 和 jvm 不关心已检查和未检查的异常,但是 process 方法在 Java 类中,因此通过 Java 编译器运行,这就是为什么我对它的有效性感到困惑代码。
    • "Only the Java compiler cares this here"被认为是为了明确我从这里开始谈论 Java 编译器的句子。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2013-07-24
    • 2016-04-03
    • 1970-01-01
    • 2013-06-08
    • 2011-02-21
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多