简体   繁体   English

“专门化”一个子类型的方法来抛出一个已检查的异常?

[英]“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: 现在我看到两种解决方案:

  • define ProductSearcher.search(String) to throw a RemoteMadeProblemsException . 定义ProductSearcher.search(String)以抛出RemoteMadeProblemsException
  • or make 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: 我觉得这两种方案都不合适

  • the first solution eg forces any user of DatabaseProductSearcher.search to catch/throw a RemoteMadeProblemsException which doesn't make sense (it's a local database after all). 第一个解决方案例如强制DatabaseProductSearcher.search 任何用户捕获/抛出一个没有意义的RemoteMadeProblemsException (毕竟它是一个本地数据库)。
  • the second solution opens the door for sloppy programming. 第二种解决方案为马虎编程打开了大门。 Eg someone uses 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. 然后,您的OnlineSearcherExceptionOnlineSearcherException ProductSearcherException并且您的throw声明变得合法。

One thing you can do to improve things involves having three classes instead of one: 你可以做的一件事就是改进事物包括三个班而不是一个班:

  • Your base ProductSearcher which declares the method as throwing the exception 你的基础ProductSearcher声明方法抛出异常
  • Your local implementation which does not throw the exception 您的本地实现不会抛出异常
  • The remote implementation which does throw the exception (or a more specialized 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.

相关问题 Java检查异常不在函数的throw规范中? - Java checked exception not in the function's throw specification? 当方法中没有代码会抛出检查异常时声明“抛出异常” - Declaring 'throws Exception' when no code in a method would throw checked exception 抛出一个检查异常 - Throw a checked exception SonarQube错误:重构此方法最多抛出一个已检查的异常 - SonarQube error: Refactor this method to throw at most one checked exception 如果方法无法引发声明的已检查异常,则配置Java编译器是否出错 - Configure Java Compiler for Error if method cannot throw declared checked Exception 重构方法以引发最多一个检查的异常,而不是ExecutionException和InterruptedException - Refactor method to throw at most one checked exception instead of ExecutionException and InterruptedException Java Thread:Run方法不能抛出已检查的异常 - Java Thread: Run method cannot throw checked exception 使用 Java 可选 ifPresentOrElse 方法时无法抛出 `checked` 异常 - Unable to throw a `checked` exception when using Java Optional ifPresentOrElse method 是否需要在实现中抛出检查异常,但接口没有抛出该异常? - Need to throw checked exception in implementation, but it's not thrown by the interface? 在Java方法中抛出特定异常是否多余? - Is it redundant to throw specific exception in Java's method?
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM