38

一般来说,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。

据我所知,没有特定的注释表明方法总是抛出。

4

6 回答 6

47

一个简单的解决方法是让您的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是不太可能的,这正是因为编译器通常会抱怨。

于 2013-02-22T08:18:51.260 回答
10

我使用的一个技巧是替换

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 的返回类型如何,它都可以工作,包括原始类型。您仍然可以fatalISE在 void 方法中使用,只是不使用return关键字。

于 2013-02-22T08:15:52.537 回答
8

反转if条件怎么样?

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

祝你好运!

于 2013-02-22T08:24:19.617 回答
6

添加

return null;

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

于 2013-02-22T08:19:12.553 回答
3

您要做的是声明一个函数永远不会返回,这不是 Java 知道如何做的事情。相比之下,Kotlin 有一个 Nothing 类型。如果你声明你的函数返回“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 等)的地方执行此操作。
  • 如果您使用容易出错(一个出色的静态 Java 错误检查工具),则两个 @-annotations 是必需的,因为否则它会在调用站点抱怨您没有使用返回值,并且它会在方法定义中抱怨你正在用 type 参数做无法形容的邪恶事情。
于 2019-01-07T21:15:01.837 回答
1

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

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

或者更好地将其替换为throw new AssertionError()防止返回 null 值,提高可读性并确保如果有人修改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();
    }
}
于 2017-11-16T14:45:23.640 回答