[英]“Specializing” a subtype's method to throw a checked exception?
In my Java project, I have the following class / interface hierarchy: 在我的Java项目中,我有以下类/接口层次结构:
public interface ProductSearcher {
Set<Product> search(String request);
}
public interface OnlineProductSearcher extends ProductSearcher {
}
public interface DatabaseProductSearcher extends ProductSearcher {
}
The OnlineProductSearcher
searches for products at some remote machine (eg an implementation uses HTTP), while the DatabaseProductSearcher
searches for products within my local machine (eg an implementation uses JPA). OnlineProductSearcher
在某个远程机器上搜索产品(例如,实现使用HTTP),而DatabaseProductSearcher
在我的本地机器中搜索产品(例如,实现使用JPA)。
As it turns out, from time to time, the OnlineProductSearcher
may have problems searching for products because the remote machine is down, is rate-limiting my requests, responses with 5xx, 4xx, and whatnot. 事实证明,
OnlineProductSearcher
可能在搜索产品时遇到问题,因为远程机器已关闭,我的请求是速率限制的,5xx,4xx和诸如此类的响应。
So I had the idea to have my OnlineProductSearcher
implementations throw an RemoteMadeProblemsException
whenever there is a problem related to the remote machine. 所以我有想法让我的
OnlineProductSearcher
实现在出现与远程机器相关的问题时抛出RemoteMadeProblemsException
。
And as I want to force any OnlineProductSearcher
user to handle these exception gracefully and not forget to do so, I made RemoteMadeProblemsException
a checked exception , ie RemoteMadeProblemsException extends Exception
. 因为我想强制任何
OnlineProductSearcher
用户优雅地处理这些异常而不要忘记这样做,我使RemoteMadeProblemsException
成为一个已检查的异常 ,即RemoteMadeProblemsException extends Exception
。
So I went along and had the idea to redefine OnlineProductSearcher
like this: 所以我一直认为重新定义
OnlineProductSearcher
是这样的:
public interface OnlineProductSearcher extends ProductSearcher {
Set<Product> search(String request) throws RemoteMadeProblemsException;
}
But in Java, it is not possible to redeclare/constrain methods from a supertype inside a subtype (Eclipse tells me " Exception RemoteMadeProblemsException is not compatible with throws clause in ProductSearcher.search(String) ") 但是在Java中,不可能从子类型中的超类型重新声明/约束方法(Eclipse告诉我“ Exception RemoteMadeProblemsException与ProductSearcher.search(String)中的throws子句不兼容 ”)
Now I see two solutions to this situations: 现在我看到两种解决方案:
ProductSearcher.search(String)
to throw a RemoteMadeProblemsException
. ProductSearcher.search(String)
以抛出RemoteMadeProblemsException
。 RemoteMadeProblemsException
extend RuntimeException
and don't have OnlineProductSearcher.search(String)
declare a throws
clause. RemoteMadeProblemsException
扩展RuntimeException
并且没有OnlineProductSearcher.search(String)
声明一个throws
子句。 I find both solutions inadequate: 我觉得这两种方案都不合适
DatabaseProductSearcher.search
to catch/throw a RemoteMadeProblemsException
which doesn't make sense (it's a local database after all). DatabaseProductSearcher.search
任何用户捕获/抛出一个没有意义的RemoteMadeProblemsException
(毕竟它是一个本地数据库)。 OnlineProductSearcher.search(String)
and forgets to try-catch a RemoteMadeProblemsException
, letting the exception fall through and ripple up. OnlineProductSearcher.search(String)
并忘记尝试捕获RemoteMadeProblemsException
,让异常失败并OnlineProductSearcher.search(String)
。 What are better solutions to this "only some subtype may throw an exception" problem? 什么是更好的解决方案“只有一些子类型可能会抛出异常”的问题?
The problem you have is this: 你遇到的问题是:
ProductSearcher x = new OnlineProductSearcher();
This is entirely legal syntax and now if someone calls x.method()
there is no way for Java to know about that checked exception. 这是完全合法的语法,现在如果有人调用
x.method()
,Java就无法知道该检查的异常。
This is why subclasses can only make implementations more specific. 这就是为什么子类只能使实现更具体。 They can return subclasses and accept super classes but not the other way around.
他们可以返回子类并接受超类,但不能反过来。 This is because the requirement is that any call made to the super method is also valid against the subclass method.
这是因为要求对super方法的任何调用对子类方法也是有效的。
For example if: 例如,如果:
Number process(Integer i) {
}
is a super class then a valid subclass is: 是一个超类然后一个有效的子类是:
Integer process(Number i) {
}
Because every call to process
in the super class is also valid in the sub class. 因为在超类中对每个
process
调用在子类中也是有效的。 The exact same argument applies to throws
declarations. 完全相同的参数适用于
throws
声明。 By making the sub class throw a checked exception you make it impossible to treat it as a method with the same signature as in the super class. 通过使子类抛出一个已检查的异常,您无法将其视为具有与超类中相同签名的方法。
The solution to your dilemma is to define a more generic exception ProductSearcherException
and have ProductSearcher
throw that exception. 解决您的困境的方法是定义一个更通用的异常
ProductSearcherException
并让ProductSearcher
抛出该异常。
Your OnlineSearcherException
then subclasses ProductSearcherException
and your throw declaration becomes legal. 然后,您的
OnlineSearcherException
将OnlineSearcherException
ProductSearcherException
并且您的throw声明变得合法。
One thing you can do to improve things involves having three classes instead of one: 你可以做的一件事就是改进事物包括三个班而不是一个班:
This does weaken the ability for people to do ProductSearcher x = new LocalProductSearcher
and then use the more generic class (as then they would need to catch the exception) but for anyone using LocalProductSearcher
throughout they would never need to do the catch. 这确实削弱了人们做
ProductSearcher x = new LocalProductSearcher
,然后使用更通用的类(因为他们需要捕获异常),但对于任何使用LocalProductSearcher
,他们永远不需要捕获。
Note though that even in the local case you may find yourself needing to throw exceptions in the future so having them is not terrible. 请注意,即使在本地情况下,您可能会发现自己需要在将来抛出异常,因此拥有它们并不可怕。
IMHO You should add the throws Exception
to your superinterface (and probably some more refined version, not just Exception). 恕我直言你应该将
throws Exception
添加到你的超级接口(可能还有一些更精致的版本,而不仅仅是Exception)。
The reasoning is that if you do not declare throws exception, then your contract is: Under normal execution no implementation of this method should throw an exception. 原因是如果你没有声明throws异常,那么你的契约是:在正常执行下,没有实现这个方法应该抛出异常。 In your program this is clearly not the case, as the remote may experience problems.
在你的程序中,情况显然并非如此,因为遥控器可能会遇到问题。
Your reason for not adding an exception to the database version is silly: The DB is local today, but may be remote tomorrow. 你不在数据库版本中添加例外的原因是愚蠢的:数据库今天是本地的,但明天可能是远程的。 And local DBs can have problems too.
而本地数据库也可能存在问题。
If you REALLY don't want to catch exceptions from the DatabaseProductSearcher version, then you need to reference it as itself, not as the superinterface, and redefine the method in DatabaseProductSearcher to not throw anything. 如果您真的不想从DatabaseProductSearcher版本中捕获异常,那么您需要将其作为自身引用,而不是作为超接口引用,并重新定义DatabaseProductSearcher中的方法以不抛出任何内容。 Then when you refer to it by that interface, you are not forced to catch anything, as the compiler now knows that this version is safe.
然后,当您通过该接口引用它时,您不会被强制捕获任何内容,因为编译器现在知道此版本是安全的。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.