为什么Java在实现2个接口时只允许单个方法X,这两个接口都声明X但是它们的throws子句有所不同?

时间:2011-04-12 06:17:39

标签: java interface duplicates

这与以下问题密切相关:Java - Method name collision in interface implementation

当有两个Java接口共享一个方法时,几乎相同的签名是这样的:

import java.io.IOException;

interface ISomething1 {
    void doSomething();
}

interface ISomething2 {
    void doSomething() throws IOException;
}

class Impl implements ISomething1, ISomething2 {
  public void doSomething() {
    throw new IOException(); // this does not compile since ISomething1.doSomething does not declare a throws clause
  }

  public static void main(String args[]) throws IOException {
    Impl i = new Impl();
    i.doSomething();
  }
}

为什么Java强制执行只有一个方法实现这两种接口方法,即使技术上它们的签名不同?

另请注意,在上面的示例中,Impl.doSomething的实现不需要抛出IOException,否则编译器会抱怨ISomething1.doSomething还必须声明它会有效地抛出IOException并生成throws子句在ISomething2.doSomething无用。

我错过了一些明显的东西吗?提前感谢您的回复。

5 个答案:

答案 0 :(得分:2)

在子类中实现方法时,可以隐藏或抛出从基类“扩展”异常的异常。

在你的情况下:

public void doSomething() {
    throw new IOException(); // this does not compile since ISomething1.doSomething does not declare a throws clause
  }

IOException不是运行时异常,应该在方法的签名中声明。 但是这个:

public void doSomething() throws IOException {
    throw new IOException(); // this does not compile since ISomething1.doSomething does not declare a throws clause
  }

不遵守所描述的规则,因

而无效
interface ISomething1 {
    void doSomething();
}

P.S。这些不是不同的签名。 如果要抛出新的异常并扩展两个接口 - 可以用下一种方式封装IOException:

throw new RuntimeException(new IOException());

<强>更新 但这很危险,因为类(程序员)的用户没有强制执行以捕获此异常,这可能会破坏最终用户的体验。

答案 1 :(得分:2)

您可能希望编译器根据客户端代码是否处理异常来了解您的客户端代码是否打算调用void doSomething();void doSomething() throws IOException;

但Java编译器并不那么聪明。

(当然,如果抛出的异常是RuntimeException,无论编译器有多聪明,都无法推断出客户端想要调用哪种方法。

构成方法签名的类型的唯一内容是名称和参数。

答案 2 :(得分:1)

方法签名不包括抛出的异常或返回值类型。因此,对于编译器,两个接口都声明了相同的方法。

答案 3 :(得分:0)

通过使用通用名称,您已经有效地尝试组合两个不相关的方法。一种方法执行IO操作,另一种方法似乎不必或必须处理它自己的错误。你必须决定它是哪一个以及该方法应该做什么。

你有一个混乱的暗示是你有两个接口,但你不能在任何地方使用它们。即你可以删除它们,你的main()就可以正常编译。

答案 4 :(得分:0)

首先,我希望这更像是一个学术问题,而不是你实际遇到的问题。如果没有,答案很可能是通过在单个类中实现两个接口而犯了错误。

但要回答你的问题......让我们说有些代码需要一个ISomething1类型的对象,因为它需要用它做一些事情。如果你的Impl类实际上抛出异常,那么调用代码将完全没有准备好处理异常。

相反的情况并非如此,因此编译器强迫你这样做。