在try语句块中使用return关键字

4
我希望请你就如何最好地管理这种情况给予建议:
public Unit createUnit(final int unitLevel) {
    final List<String> widgetClassNames = getUnitsClasses();
    try {
        return (Unit) Class.forName(widgetClassNames.get(unitLevel))
        .getConstructor(Player.class).newInstance(getOwner().get());
    } catch (InstantiationException | IllegalAccessException | IllegalArgumentException | InvocationTargetException
            | NoSuchMethodException | SecurityException | ClassNotFoundException e) {
        e.printStackTrace();
    }
    return null;

这个方法创建一个单元并返回它,但是我不喜欢在发生异常时返回null。我该如何解决?

提前感谢!


4
你可以重新抛出错误并在更高层次的地方捕获它。 - Andronicus
1
在异常情况下,返回什么数据才是有效的?如果抛出异常,调用者会期望得到数据。或者你希望程序结束吗? - Carcigenicate
2个回答

4
创建一个新的适当的Exception,可以管理自定义数据,例如输入的unitLevel值。
public class UnitCreationException extends Exception {
   ...

   // Package-private constructor.
   // Don't expose too much to the outer world
   UnitCreationException(
        final String message,
        final int unitLevel,
        final Throwable cause) { ... }
}

在代码中直接捕获 Exception,记录下你觉得可能有用且只能在此处获取的信息。

public Unit createUnit(final int unitLevel) throws UnitCreationException {
    final List<String> widgetClassNames = getUnitsClasses();

    try {
        return (Unit) Class.forName(widgetClassNames.get(unitLevel))
                           .getConstructor(Player.class)
                           .newInstance(getOwner().get());
    } catch (InstantiationException 
            | IllegalAccessException 
            | IllegalArgumentException 
            | InvocationTargetException
            | NoSuchMethodException 
            | SecurityException 
            | ClassNotFoundException e) {
        // Do not log the Exception stack-trace here, you'll do that
        // at a hihger level
        logger.error("Error message");

        // Re-throw your custom and more meaningful Exception
        throw new UnitCreationException("Error message", unitLevel, e);
    }
}

在更高的层面上,你将被迫捕获那个 UnitCreationException

try {
   final Unit unit = createUnit(10);
} catch (final UnitCreationException e) {
   // Here you can log the Exception stack-trace
   logger.error("Error message", e);

   // Do something with the information contained in the custom Exception
   final int erroredUnitLevel = e.getUnitLevel();
   ...
}

关于检查型异常和非检查型异常总是有争论。我觉得这是检查型异常的一个很好的应用场景,尤其是当这是可以被多个客户端使用的库的一部分时。

你希望能够以某种方式从这样的错误中恢复,而不会使整个应用程序停止(也许通过重试逻辑)。


我见过开发人员使用 Optional<T> 作为返回类型,只是为了避免 null。这是 适得其反 的,因为你失去了真正的错误原因,而且你不能在自己的分支中做出反应(也就是 catch 分支)。

最终,Java 提供了一种定制 Exception 的方法,所以请使用这个特性。



0

最重要的是确保你的调用者得到createUnit()调用失败的信息。你已经说过,null不是一个好的选择,我完全同意。

最好的方法是向你的调用者抛出异常,但是使用哪种异常呢?

  • 原始异常?

  • 还是新创建的异常?

我们应该考虑哪些方面?

  • 如果您在代码中添加(无效的)错误处理行,将导致代码可读性下降。相反,您应该专注于真正想要做的事情 - 这已经足够复杂了。因此,任何可以避免使用 try/catch/throw 而又不影响健壮性的代码都应该删除。
  • 您的调用者需要知道调用失败的信息,这就是异常的用途,所以他应该得到一个异常处理。
  • 一个问题应该只被记录一次,在某个顶层或服务边界处。为了支持这一点,异常应该包含后续问题分析所需的信息。Java系统异常已经携带了大量有用的信息,但有时您需要添加更多上下文信息。没有必要在较低级别记录错误。
  • 调用方是否会编写依赖于异常类型的代码,例如尝试/捕获块,对不同的异常类型有不同的反应?他甚至能做到这一点吗?/应该了解您方法的内部工作原理,以便在这种或那种异常情况下知道该怎么做吗?通常不会。因此,异常类通常不重要。
  • 应用程序将如何最终通知其用户某个功能已失败?您的异常能否协助完成此任务,例如提供适当的信息?但是您是否知道哪些措辞可能适合调用方的应用程序?因此,我怀疑您在这方面可以提供帮助。
所以,我认为将低级别异常包装在高级别异常中的唯一原因是,如果您想添加有用于日志记录的上下文信息(或者某个固定的方法签名强制您翻译原始异常)。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接