【问题标题】:Why is throws part of the method signature为什么会抛出方法签名的一部分
【发布时间】:2015-10-07 11:34:36
【问题描述】:

为什么throws 在方法上是其签名的一部分?包含它似乎很奇怪。这是一个阻碍的例子:

@Overide
public void foo() {
    throw new UnsupportedOperationException();
}

如果有人从外部看到此方法,他们可能会在不知道它不受支持的情况下尝试使用它。他们只会在尝试运行代码时学习它。

但是,如果他们可以做这样的事情,他们会通过查看不受支持的方法知道,如果 UnsupportedOperationException 没有扩展 RuntimeException,他们会收到编译错误。 EDIT1:但这是不可能的,因为throws 是签名的一部分,因此覆盖不起作用。

@Overide
public void foo() throws UnsupportedOperationException {
    throw new UnsupportedOperationException();
}

这个问题与 Java 的设计有关,所以我知道如果没有一个从事它工作的人过来回答它,可能很难回答,但我希望这个问题可能以前被问过,或者那个用这种方式解释原因可能有一个明显的理由。

【问题讨论】:

  • 您似乎在这里回答了您自己的问题?它在签名上,因为现在您(和编译器)知道代码会抛出异常并且需要处理它?
  • 未经检查的异常和它们被抛出的条件应该记录在 javadoc 中。这种范式用于内置 Java 库,并被多本书籍推荐,例如有效的 Java 就是其中之一。正如您正确指出的那样,编译器不需要在签名中声明它们。
  • 你要问的是是否抛出未经检查的异常。首先,您的方法使用@Override,这表明它应该遵循合同(抽象或接口),但对于未经检查的异常,您没有义务这样做。无论您选择抛出它还是不总是遵循@danstahr 提到的良好做法,请将其添加到 javadoc 中。
  • 未经检查的异常通常用于几乎无处不在的问题。想想NullPointerExceptionArrayIndexOutOfBoundsException。我不想将它们包含在每个方法签名中。
  • 关于声明 RuntimeExceptions 被抛出另见stackoverflow.com/questions/8396329/…

标签: java exception methods method-signature


【解决方案1】:

throws 部分并不表示该方法需要抛出上述异常,即使在特定情况下也不需要。它只告诉函数允许这样做。

因此包含throws UnsupportedOperationException 并不意味着该方法不受支持。除了UnsupportedOperationExceptionRuntimeException 之外,一个方法也可能是throw

现在由于在方法的签名中需要它,它归结为完全检查异常的能力。为了让编译器能够决定一个方法是否只能抛出指定的异常,它必须能够决定它调用的方法不能抛出未捕获的异常。

这意味着,例如,重写一个方法意味着您不能添加可能抛出的异常,否则您将破坏验证调用该方法的方法不能抛出其指定之外的任何其他内容的可能性。另一种方法是可能的(但我不确定Java是否支持),用一个可能不会抛出的方法覆盖一个可能抛出的方法。

例如:

class B {
    int fubar(int) throws ExceptionA {
    }

    int frob(int) throws ExceptionA {
         return fubar(int);
    }
}

class D extends B {
    int fubar(int) throws ExceptionB {
    }
}
    

现在frob 可能只指定为throw ExceptionA,但在调用this.fubar 时可能会抛出其他东西,但fubar 可能只定义为throw @987654333 @。这就是为什么D.fubar 是一个无效的覆盖,因为这会打开this.fubar 实际抛出ExceptionB 的可能性,并且编译器将无法保证frob 不会抛出ExceptionB

【讨论】:

    【解决方案2】:

    Java 有两种不同类型的异常:checked 异常和 unchecked 例外。

    未经检查的异常是RuntimeException 的子类,您不必添加 throws 声明。所有其他异常都必须在方法体中处理,要么使用 try/catch 语句,要么使用 throws 声明。

    未经检查的异常示例:IllegalArgumentException 有时用于通知已使用非法参数调用方法。无需投掷。

    检查异常的示例:IOException java.io 包中的某些方法可能会抛出。使用 try/catch 或在方法声明中添加 throws IOException 并将异常处理委托给方法调用者。

    【讨论】:

      【解决方案3】:

      没有人提到的一件事是对您的问题的非常重要的回答:

      为什么在方法上抛出是其签名的一部分?

      throws 不是方法签名的一部分。

      JLS 8.4.2. Method Signature 说得很清楚:

      两个方法或构造函数,M 和 N,具有相同的签名 如果它们具有相同的名称、相同的类型参数(如果有)(第 8.4.4 节),并且在调整了N 到 M 的类型参数,形参类型相同


      如果有人从外面看到这个方法,他们可能会在不知道它不受支持的情况下尝试使用它。

      这正是为什么throws 被设计成在编译时检查所有检查的异常的主要原因 - 该方法的客户将知道该方法可能会抛出什么.


      Checked Exceptions 被强制处理或指定抛出(但此规范不是方法签名的一部分)。

      未经检查的异常不必处理或指定要抛出,Oracle's tutorial 擅长解释 - 为什么:

      运行时异常表示由编程问题引起的问题,因此,不能合理地期望 API 客户端代码从它们中恢复或以任何方式处理它们。此类问题包括算术异常,例如除以零;指针异常,例如试图通过空引用访问对象;和索引异常,例如尝试通过太大或太小的索引访问数组元素。

      【讨论】:

        猜你喜欢
        • 2014-12-25
        • 1970-01-01
        • 2019-12-07
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-07-07
        相关资源
        最近更新 更多