"专业"抛出已检查异常的子类型方法?

时间:2014-05-01 17:05:51

标签: java exception exception-handling

在我的Java项目中,我有以下类/接口层次结构:

public interface ProductSearcher {
    Set<Product> search(String request);
}

public interface OnlineProductSearcher extends ProductSearcher {
}

public interface DatabaseProductSearcher extends ProductSearcher {
}

OnlineProductSearcher在某个远程计算机上搜索产品(例如,实现使用HTTP),而DatabaseProductSearcher搜索本地计算机中的产品(例如,实现使用JPA)。

事实证明,OnlineProductSearcher可能会不时搜索产品,因为远程计算机已关闭,我的请求是速率限制,5xx,4xx和其他的响应。

所以,当有与远程机器相关的问题时,我有一个想法让我的OnlineProductSearcher实现抛出RemoteMadeProblemsException
由于我想强制任何OnlineProductSearcher用户优雅地处理这些异常并且不要忘记这样做,我将RemoteMadeProblemsException作为已检查的异常,即RemoteMadeProblemsException extends Exception

所以我一直认为要像这样重新定义OnlineProductSearcher

public interface OnlineProductSearcher extends ProductSearcher {
    Set<Product> search(String request) throws RemoteMadeProblemsException;
}

但是在Java中,不可能从子类型中的超类型重新声明/约束方法(Eclipse告诉我&#34; 异常RemoteMadeProblemsException与throws子句不兼容      ProductSearcher.search(字符串) &#34)

现在我看到两种解决方案:

  • 定义ProductSearcher.search(String)以抛出RemoteMadeProblemsException
  • 或使RemoteMadeProblemsException延长RuntimeException,并且OnlineProductSearcher.search(String)没有声明throws条款。

我发现两种解决方案都不合适:

  • 第一个解决方案,例如强迫 DatabaseProductSearcher.search的任何用户捕获/抛出一个没有意义的RemoteMadeProblemsException(毕竟它是一个本地数据库)。
  • 第二个解决方案为马虎编程打开了大门。例如。有人使用OnlineProductSearcher.search(String)并忘记尝试抓住RemoteMadeProblemsException,让异常落空并涟漪。

对此有什么更好的解决方案&#34;只有某些子类型可能会抛出异常&#34;问题

2 个答案:

答案 0 :(得分:4)

你遇到的问题是:

  ProductSearcher x = new OnlineProductSearcher();

这是完全合法的语法,现在如果有人调用x.method(),Java就无法知道该检查的异常。

这就是为什么子类只能使实现更具体。他们可以返回子类并接受超类,但不能反过来。这是因为要求对super方法的任何调用对子类方法也是有效的。

例如:if:

Number process(Integer i) {
}

是一个超类,然后一个有效的子类是:

Integer process(Number i) {
}

因为超类中对process的每次调用在子类中也是有效的。完全相同的参数适用于throws声明。通过使子类抛出一个已检查的异常,您无法将其视为具有与超类中相同签名的方法。

解决您的困境的方法是定义一个更通用的异常ProductSearcherException并让ProductSearcher抛出该异常。

您的OnlineSearcherException然后子类ProductSearcherException和您的投票声明变得合法。

你可以做的一件事就是改进事物包括三个班而不是一个班:

  • 您的基础ProductSearcher,它将该方法声明为抛出异常
  • 您的本地实现不会抛出异常
  • 抛出异常(或更专业的异常)的远程实现。

这确实削弱了人们执行ProductSearcher x = new LocalProductSearcher然后使用更通用的类的能力(因为他们需要捕获异常)但对于任何使用LocalProductSearcher的人来说,他们永远不需要抓住了。

请注意,即使在本地情况下,您可能会发现自己需要在将来抛出异常,因此使用它们并不可怕。

答案 1 :(得分:0)

恕我直言你应该将throws Exception添加到你的超级接口(可能还有一些更精致的版本,而不仅仅是Exception)。

原因是如果你没有声明throws异常,那么你的契约是:在正常执行下,没有实现这个方法应该抛出异常。在你的程序中,情况显然不是这样,因为遥控器可能会遇到问题 你不在数据库版本中添加例外的原因是愚蠢的:数据库今天是本地的,但明天可能是远程的。而本地数据库也可能存在问题。

如果您真的不想从DatabaseProductSearcher版本中捕获异常,那么您需要将其作为自身引用,而不是作为超接口引用,并重新定义DatabaseProductSearcher中的方法以不抛出任何内容。然后,当您通过该接口引用它时,您不会被强制捕获任何内容,因为编译器现在知道此版本是安全的。