在我的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;问题
答案 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 x = new LocalProductSearcher
然后使用更通用的类的能力(因为他们需要捕获异常)但对于任何使用LocalProductSearcher
的人来说,他们永远不需要抓住了。
请注意,即使在本地情况下,您可能会发现自己需要在将来抛出异常,因此使用它们并不可怕。
答案 1 :(得分:0)
恕我直言你应该将throws Exception
添加到你的超级接口(可能还有一些更精致的版本,而不仅仅是Exception)。
原因是如果你没有声明throws异常,那么你的契约是:在正常执行下,没有实现这个方法应该抛出异常。在你的程序中,情况显然不是这样,因为遥控器可能会遇到问题 你不在数据库版本中添加例外的原因是愚蠢的:数据库今天是本地的,但明天可能是远程的。而本地数据库也可能存在问题。
如果您真的不想从DatabaseProductSearcher版本中捕获异常,那么您需要将其作为自身引用,而不是作为超接口引用,并重新定义DatabaseProductSearcher中的方法以不抛出任何内容。然后,当您通过该接口引用它时,您不会被强制捕获任何内容,因为编译器现在知道此版本是安全的。