簡體   English   中英

“專門化”一個子類型的方法來拋出一個已檢查的異常?

[英]“Specializing” a subtype's method to throw a checked exception?

在我的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告訴我“ Exception RemoteMadeProblemsException與ProductSearcher.search(String)中的throws子句不兼容 ”)

現在我看到兩種解決方案:

  • 定義ProductSearcher.search(String)以拋出RemoteMadeProblemsException
  • 或者使RemoteMadeProblemsException擴展RuntimeException並且沒有OnlineProductSearcher.search(String)聲明一個throws子句。

我覺得這兩種方案都不合適

  • 第一個解決方案例如強制DatabaseProductSearcher.search 任何用戶捕獲/拋出一個沒有意義的RemoteMadeProblemsException (畢竟它是一個本地數據庫)。
  • 第二種解決方案為馬虎編程打開了大門。 例如,有人使用OnlineProductSearcher.search(String)並忘記嘗試捕獲RemoteMadeProblemsException ,讓異常失敗並OnlineProductSearcher.search(String)

什么是更好的解決方案“只有一些子類型可能會拋出異常”的問題?

你遇到的問題是:

  ProductSearcher x = new OnlineProductSearcher();

這是完全合法的語法,現在如果有人調用x.method() ,Java就無法知道該檢查的異常。

這就是為什么子類只能使實現更具體。 他們可以返回子類並接受超類,但不能反過來。 這是因為要求對super方法的任何調用對子類方法也是有效的。

例如,如果:

Number process(Integer i) {
}

是一個超類然后一個有效的子類是:

Integer process(Number i) {
}

因為在超類中對每個process調用在子類中也是有效的。 完全相同的參數適用於throws聲明。 通過使子類拋出一個已檢查的異常,您無法將其視為具有與超類中相同簽名的方法。

解決您的困境的方法是定義一個更通用的異常ProductSearcherException並讓ProductSearcher拋出該異常。

然后,您的OnlineSearcherExceptionOnlineSearcherException ProductSearcherException並且您的throw聲明變得合法。

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

  • 你的基礎ProductSearcher聲明方法拋出異常
  • 您的本地實現不會拋出異常
  • 拋出異常(或更專業的異常)的遠程實現。

這確實削弱了人們做ProductSearcher x = new LocalProductSearcher ,然后使用更通用的類(因為他們需要捕獲異常),但對於任何使用LocalProductSearcher ,他們永遠不需要捕獲。

請注意,即使在本地情況下,您可能會發現自己需要在將來拋出異常,因此擁有它們並不可怕。

恕我直言你應該將throws Exception添加到你的超級接口(可能還有一些更精致的版本,而不僅僅是Exception)。

原因是如果你沒有聲明throws異常,那么你的契約是:在正常執行下,沒有實現這個方法應該拋出異常。 在你的程序中,情況顯然並非如此,因為遙控器可能會遇到問題。
你不在數據庫版本中添加例外的原因是愚蠢的:數據庫今天是本地的,但明天可能是遠程的。 而本地數據庫也可能存在問題。

如果您真的不想從DatabaseProductSearcher版本中捕獲異常,那么您需要將其作為自身引用,而不是作為超接口引用,並重新定義DatabaseProductSearcher中的方法以不拋出任何內容。 然后,當您通過該接口引用它時,您不會被強制捕獲任何內容,因為編譯器現在知道此版本是安全的。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM