Java 不同接口中相同方法的冲突异常规范

Java 不同接口中相同方法的冲突异常规范,java,exception,Java,Exception,在以下代码中: class MyException extends Exception{} interface Bread { public void eat() throws MyException; } interface Fringe { public void eat() throws RuntimeException; } public class Test implements Fringe , Bread // #5 { public static

在以下代码中:

class MyException extends Exception{}

interface Bread
{
    public void eat() throws MyException;
}

interface Fringe 
{
    public void eat() throws RuntimeException;
}

public class Test implements Fringe , Bread // #5
{
    public static void main(String[] args)
    {
        Fringe best = new Test(); // #1
        best.eat();// #2
    }

    public void eat() throws RuntimeException // #3
    {
        System.out.println("Test");
        throw new RuntimeException(); // #4
    }
}

MyException
不是
RuntimeException
。为什么我们可以将
Test.eat()
声明为抛出
RuntimeException
,而不是一般的
异常

这是因为在重写方法中,您只能抛出低于或等于合同中定义的异常的异常。因此,
eat()
可以抛出
MyException
及其子类,但不能抛出
MyException
之上的任何内容,这就是
Exception
的含义。运行时异常不是此规则的一部分,您可以自由抛出它们,因为它们未被“选中”

这是因为假设我正在使用
Bread
接口访问
Test
类的实例。如果我调用
eat()
方法,我可以“检查”MyException,因为它是契约的一部分,但是如果底层实现决定抛出“更高”的异常,它将不会被捕获在我的
MyException
catch块中,从而违反契约。查看以下代码以获取示例:

Bread b = new Test();
try {
  // if this throws Exception, it won't be caught in the catch block
  // thereby violating contract
  b.eat(); 
} catch (MyException e) {
  e.printStacktrace();
}
两点:

  • 您不能将其声明为抛出
    Exception
    ,因为它会抛出比
    Bread
    接口中的
    eat()
    方法指定的更一般的异常。从超类或接口继承的方法不能抛出比在超类或接口中指定的更一般的异常
  • 您可以将其声明为抛出
    RuntimeException
    ,因为无论是否在
    throws
    子句中指定,方法都可以抛出未经检查的异常。(因此,指定它可以抛出
    RuntimeException
    是多余的)
为了解释我在第一点中提到的规则的原因:假设您这样做:

// Allowed because Test implements Bread
Bread obj = new Test();
如果现在调用
obj.eat()
,编译器需要检查您是否正确处理该调用中可能发生的所有已检查异常。它通过查看变量
obj
的类型来实现这一点,即
Bread
Bread
接口指定
eat()
可以抛出
MyException
(并且隐式地是
MyException
的子类)

如果允许您的
Test.eat()
方法抛出一种更一般的已检查异常,例如
exception
,那么编译器无法仅通过查看
obj
的类型来检查您是否正确处理了所有已检查的异常

为了防止这个问题,规则是不允许重写的方法抛出更一般的异常

重写方法只能抛出那些与重写方法中抛出的异常满足IS-A关系的异常

如果您试图在类中抛出
Exception
,而接口抛出
MyException
。。。编译器查看以下语句是否为真:

例外是——我的例外——你知道,它是假的。所以编译器拒绝了它

因此,您只能从子类中的方法引发更具体的异常

有关运行时异常的问题:

对于引发运行时异常,它相当于您没有显式编写它。所以编译器忽略了它。因为即使您没有提到它,方法也可以在运行时抛出异常


你确定这是正确的代码吗?MyException实际上并没有在这里的任何地方抛出。