【问题标题】:Is it possible to tell the compiler that a method always throws an Exception是否可以告诉编译器一个方法总是抛出一个异常
【发布时间】:2013-02-22 08:15:52
【问题描述】:

一般来说,Java 编译器不会传播方法“总是”抛出异常的信息,因此不会检测到所有代码路径是否完整。

(这是因为Java编译器独立编译每个类)。

当你想写这样的东西时,这是一个问题。

public class ErrorContext {
    public void fatalISE(String message) {
        String context = "gather lots of information about the context of the error";
        throw new IllegalStateException(context +": " + message);
    }
}

public class A {
    public MyObject myMethod() {
        if (allIsGood()) {
            return new MyObject();
        }
        ErrorContext.fatalISE("all is not good");
    }
}

(即一种收集上下文信息的“断言助手”)。

因为编译器会抱怨 myMethod 并不总是返回 MyObject。

据我所知,没有具体的注释表明方法总是抛出异常。

【问题讨论】:

  • 另一个帖子here的解决方案。

标签: java


【解决方案1】:

一个简单的解决方法是让您的fatalISE 方法不抛出异常,而只创建它:

public class ErrorContext {
    public IllegalStateException fatalISE(String message) {
        String context = "gather lots of information about the context of the error";
        return new IllegalStateException(context +": " + message);
    }
}

public class A {
    public MyObject myMethod() {
        if (allIsGood()) {
            return new MyObject();
        }
        throw ErrorContext.fatalISE("all is not good");
    }
}

这样编译器就知道不会抱怨缺少return。并且不太可能忘记使用throw,因为编译器通常会抱怨。

【讨论】:

  • 这在阅读代码时具有的优势,清晰。
  • 这个解决方案在我看来是解决 "Missing return statement" 在几种情况下最干净的方法,包括带有 switch 子句的方法,其中异常是扔在任何树枝上。它只是增加了每次使用方法编写一次 throw 关键字的开销。
【解决方案2】:

我使用的一个技巧是替换

public void fatalISE(String message) {
    String context = "gather lots of information about the context of the error";
    throw new IllegalStateException(context +": " + message);
}

public <T> T fatalISE(String message) {
    String context = "gather lots of information about the context of the error";
    throw new IllegalStateException(context +": " + message);
}

然后,在 myMethod 中,使用:

public MyObject myMethod() {
   if (allIsGood()) {
        return new MyObject();
    }
    return ErrorContext.fatalISE("all is not good");
}

无论 myMethod 的返回类型如何,它都可以工作,包括原始类型。您仍然可以在 void 方法中使用fatalISE,只是不使用return关键字。

【讨论】:

  • 这很聪明(相当聪明),但对阅读代码的人有点误导。
  • 似乎适用于返回引用但不是原语的函数:type parameters of &lt;T&gt;T cannot be determined; no unique maximal instance exists for type variable T with upper bounds int,java.lang.Object 在线 return this.fatalISE("blah");
【解决方案3】:

反转 if 条件怎么样?

public MyObject myMethod() {
    if (!allIsGood()) {
        ErrorContext.fatalISE("all is not good");
    }
    return new MyObject();
}

祝你好运!

【讨论】:

    【解决方案4】:

    添加

    return null;
    

    最后。 (无论如何它永远不会到达那里,但应该让编译器保持沉默)

    【讨论】:

      【解决方案5】:

      您要做的是声明一个函数永远不会返回,这不是 Java 知道如何做的事情。 Kotlin, for contrast, has a Nothing type。如果你声明你的函数返回“Nothing”,这意味着它永远不会返回。

      上面 Zorglub 的答案,函数返回类型参数“T”的类型参数是 Java 中最接近的。这是我最近写的东西,它记录了一个字符串,然后用相同的字符串抛出一个异常:

      /**
       * Error logging. Logs the message and then throws a {@link RuntimeException} with the given
       * string.
       *
       * @param tag String indicating which code is responsible for the log message
       * @param msg String or object to be logged
       */
      @CanIgnoreReturnValue
      @SuppressWarnings("TypeParameterUnusedInFormals")
      public static <T> T logAndThrow(String tag, Object msg) {
          String s = msg.toString();
          Log.e(tag, s);
          throw new RuntimeException(s);
      }
      

      此代码使用 Android 的标准日志记录 API,是我希望能够在任何可能需要值的地方使用的代码。我希望能够拥有一个 lambda,其主体只是对该函数的调用。这个解决方案效果很好,但值得解释一些棘手的问题:

      • 您不能在需要 Java 基本类型(int、double、boolean 等)的地方执行此操作。
      • 如果您使用error-prone(一个出色的静态 Java 错误检查工具),则两个 @-annotations 是必需的,因为否则它会在调用站点抱怨您没有使用返回值,它会抱怨方法定义表明您正在使用类型参数做不可言喻的邪恶事情。

      【讨论】:

      • 感谢您的声明您要做的是声明一个函数永远不会返回,这不是 Java 知道如何做的事情。它准确地解释了为什么编译器不能处理这种情况。
      【解决方案6】:

      我刚刚遇到了这个用例,但使用的方法应该总是抛出 2 种或更多类型的异常。

      要使代码编译,您可以添加return null;MouseEvent 所说。

      或者更好地用throw new AssertionError()替换它,防止返回空值,提高可读性并确保如果有人修改checkAndAlwaysThrowException(),它将继续总是抛出异常

      public Object myMehtod() throws ExceptionType1, ExceptionType2 {
          //....
          checkAndAlwaysThrowException();
          throw new AssertionError("checkAndAlwaysThrowException should have throw exception");
      }
      
      public void checkAndAlwaysThrowException() throws ExceptionType1, ExceptionType2 {
          if (cond) {
              throw new ExceptionType1();
          } else {
              throw new ExceptionType2();
          }
      }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2011-04-23
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2014-04-04
        • 1970-01-01
        相关资源
        最近更新 更多