Java接口声明了一个异常,但接口实现却没有抛出异常?

71

我看到了一段代码,其中接口声明了一个异常但是实现它的类既没有抛出也没有捕获这个异常,这是为什么?在Java中这样做是否合法或安全?

import java.rmi.*;
public interface MyRemote extends Remote {
    public String sayHello() throws RemoteException;
}

import java.rmi.*;
import java.rmi.server.*;
public class MyRemoteImpl extends UnicastRemoteObject implements MyRemote{
    public String sayHello() {
        return "Server says, 'Hey'";
    }
    public MyRemoteImpl() throws RemoteException {}
    public static void main (String[] args) {
        try {
             MyRemote service = new MyRemoteImpl();
             Naming.rebind("RemoteHello", service);
        } catch(Exception ex)
        {
            ex.printStackTrace();
        }
    }
}

3
你可以查看 http://www.coderanch.com/t/399874/java/java/Methods-throwing-Exception-Interface 上的讨论。 - Chetter Hummin
2
是的,这是合法的。你应该看到CHetter发布的链接,他也可以将其作为答案发布。 - Thihara
接口用于声明方法。在这里,您已经声明了一个名为sayHello的方法,它具有String返回类型,并且它将抛出RemoteException异常。在类中定义时,您不需要重新指定它会抛出异常。 - Abhinav Kulshreshtha
1
根据LSP的规定,“子类型的方法不应该抛出新的异常,除非这些异常本身就是由超类型的方法抛出的异常的子类型。” - OldSchool
1
是的,没错。在接口中,你说:实现类可能会抛出这个异常。反过来就不行了,会导致编译错误。背后的逻辑很简单:如果你只知道一组对象的超类,你应该能够正确处理异常并返回它们。例如,如果你有一个名为Foo的类,其中有一个方法getDuck(),它返回一个类型为Duck的对象,你可以在Foo的任何子类中声明getDuck(),并将返回类型声明为BlackDuck,只要BlackDuck是Duck的子类,因为你可以使用Duck变量来处理它。英语表达有些糟糕。 - DGoiko
3个回答

95
实现和扩展的一般规则是,你可以使你的新类或接口“更宽松”,但不能“更加严格”。如果你认为处理异常的要求是一种限制,那么不声明异常的实现就是更宽松的。任何编写接口代码的人都不会对你的类有困难。——Stan James
作为http://www.coderanch.com/t/399874/java/java/Methods-throwing-Exception-Interface讨论的一部分。

24

如果Java方法覆盖了父类中的另一个方法,或者实现了接口定义的方法,则它不能抛出额外的已检查异常,但可以抛出较少的异常。

public class A {
    public void thrower() throws SQLException {...}
}

public class B extends A {
    @Override
    public void thrower() throws SQLException, RuntimeException, NamingException {...}
}

SQLException可以,因为它在被覆盖的方法中已经声明。甚至可以被子类SerialException替换。

RuntimeException可以,可以在任何地方使用。

NamingException不行。它不是RuntimeException,也不在A的列表中,即使作为子类型。


1
根据 JLS §8.4.8.3 规定:“如果 m1 覆盖了 m2 …… 对于 m1 的 throws 子句中列出的每个已检查异常类型,在 m2 的 throws 子句的擦除中必须出现相同的异常类或其超类型;否则,将发生编译时错误。” - M. Prokhorov

11

Chetter Hummin的回答非常好。

有一个易于记忆的看法,就是接口的实现可以更具体,但不能更普遍。

例如,在接口void test() throws Exception中,“test可能会引发异常”。

然后实现可以是void test(),意思是“test不会抛出异常”(更具体)

或实现可以是void test() throws NullpointerException(更具体)

interface x {
    void testException() throws Exception;
}

public class ExceptionTest implements x {
    @Override
    public void testException() {   //this is fine
    }

    ////// or

    @Override
    public void testException() throws NullPointerException {  // this is fine
    }
}

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