簡體   English   中英

有沒有辦法讓Runnable的run()拋出異常?

[英]Is there a way to make Runnable's run() throw an exception?

我在實現Runnable的 class 中調用run()的方法)旨在引發異常。

但是 Java 編譯器不允許我這樣做,並建議我用 try/catch 包圍它。

問題是,通過用 try/catch 包圍它,我使那個特定的 run()變得無用。 確實想拋出那個異常。

如果我為run()本身指定throws ,編譯器會抱怨Exception is not compatible with throws clause in Runnable.run()

通常我完全可以不讓run()拋出異常。 但是我有一個獨特的情況,我必須擁有該功能。

我該如何解決這個限制?

您可以使用Callable相反,它提交給ExecutorService ,並等待結果FutureTask.isDone()的返回ExecutorService.submit()

isDone()返回true時,您調用FutureTask.get() 現在,如果你的Callable拋出了一個Exception那么FutureTask.get()也會拋出一個Exception並且你可以使用Exception.getCause()來訪問原始的Exception。

如果run()拋出一個已檢查的異常,那會有什么結果呢? 您無法在處理程序中包含該run()調用,因為您沒有編寫調用它的代碼。

您可以在run()方法中捕獲已檢查的異常,並在其位置拋出未檢查的異常(即RuntimeException )。 這將使用堆棧跟蹤終止線程; 也許這就是你所追求的。

如果您希望run()方法在某處報告錯誤,那么您可以為run()方法的catch塊提供一個回調方法來調用; 該方法可以在某處存儲異常對象,然后您感興趣的線程可以在該位置找到該對象。

如果你想將一個實現Runnable的類傳遞給Thread框架,那么你必須遵循該框架的規則,參見Ernest Friedman-Hill的答案,為什么這樣做是一個壞主意。

不過,我有一種預感,你想直接在代碼中調用run方法,因此你的調用代碼可以處理異常。

這個問題的答案很簡單。 不要使用Thread庫中的Runnable接口,而是使用修改后的簽名創建自己的接口,該簽名允許拋出已檢查的異常,例如

public interface MyRunnable
{
    void myRun ( ) throws MyException;
}

您甚至可以創建一個適配器,將此接口轉換為適合在Thread框架中使用的真實Runnable (通過處理已檢查的異常)。

是的,有一種方法可以從run()方法中拋出一個已檢查的異常,但它太可怕了,我不會分享它。

這是你可以做的事情; 它使用與運行時異常相同的機制:

@Override
public void run() {
  try {
    /* Do your thing. */
    ...
  } catch (Exception ex) {
    Thread t = Thread.currentThread();
    t.getUncaughtExceptionHandler().uncaughtException(t, ex);
  }
}

正如其他人所指出的,如果你的run()方法確實是Thread的目標,那么拋出異常是沒有意義的,因為它是不可觀察的; 拋出異常與不拋出異常(無)具有相同的效果。

如果它不是Thread目標,請不要使用Runnable 例如,也許Callable更適合。

@FunctionalInterface
public interface CheckedRunnable<E extends Exception> extends Runnable {

    @Override
    default void run() throws RuntimeException {
        try {
            runThrows();
        }
        catch (Exception ex) {
            throw new RuntimeException(ex);
        }
    }

    void runThrows() throws E;

}

有些人試圖說服你必須遵守規則。 聽着,但不管你是否服從,你應該根據自己的情況決定自己。 現實是“你應該遵守規則”(而不是“你必須遵守規則”)。 請注意,如果您不遵守規則,可能會產生后果。

這種情況不僅適用於Runnable的情況,而且在Streams和其他已引入功能接口的地方也非常頻繁地使用Java 8,而無法處理已檢查的異常。 例如, ConsumerSupplierFunctionBiFunction等都已聲明,沒有設施來處理已檢查的異常。

那么情況和選擇是什么? 在下面的文本中, Runnable代表沒有聲明異常的任何功能接口,或者聲明對於手頭的用例來說太有限的異常。

  1. 你自己已經在某個地方聲明了Runnable ,並且可以用其他東西替換Runnable
    1. 考慮用Callable<Void>替換Runnable 基本上是一樣的,但允許拋出異常; 並且最后必須return null ,這是一個輕微的煩惱。
    2. 考慮用您自己的自定義@FunctionalInterface替換Runnable ,它可以准確地拋出您想要的那些異常。
  2. 您已經使用了API,並且可以使用其他選項。 例如,某些Java API被重載,因此您可以使用Callable<Void>而不是Runnable
  3. 您使用過API,但沒有其他選擇。 在這種情況下,你仍然沒有選擇。
    1. 您可以在RuntimeException包裝異常。
    2. 您可以使用未經檢查的強制轉換將異常破解為RuntimeException。

您可以嘗試以下方法。 這有點像黑客,但有時黑客是我們需要的。 因為,是否應該檢查或取消選中例外是由其類型定義的,但實際上應該根據情況來定義。

@FunctionalInterface
public interface ThrowingRunnable extends Runnable {
    @Override
    default void run() {
        try {
            tryRun();
        } catch (final Throwable t) {
            throwUnchecekd(t);
        }
    }

    private static <E extends RuntimeException> void throwUnchecked(Throwable t) {
        throw (E) t;
    }

    void tryRun() throws Throwable;
}

我比new RuntimeException(t)更喜歡它,因為它有一個較短的堆棧跟蹤。

你現在可以這樣做:

executorService.submit((ThrowingRunnable) () -> {throw new Exception()});

免責聲明:在Java的未來版本中,當不僅在編譯時處理泛型類型信息,而且在運行時處理泛型類型信息時,實際上可以刪除以這種方式執行未經檢查的強制轉換的能力。

我認為聽眾模式可能會幫助您解決這個問題。 如果在run()方法中發生異常,請使用try-catch塊並在catch中發送異常事件的通知。 然后處理您的通知事件。 我認為這將是一種更清潔的方法。 這個SO鏈接為您提供指向該方向的有用指針。

是的,您可以從 run() 方法中拋出已檢查的異常。 可以通過欺騙編譯器使用 generics 來完成。 看看這段代碼:

public static void main(String[] args) {

    new Main().throwException();

}

public void throwException() {
    Runnable runnable = () -> throwAs(new Exception());

    new Thread(runnable).start();
}

private  <T extends Throwable> void throwAs(Throwable t) throws T {
    throw ( T ) t;
}

如果您想從 Runnable 的 run() 方法中拋出已檢查的異常,這可能會有所幫助

你的要求沒有任何意義。 如果要通知線程調用者發生的異常,可以通過回調機制來實現。 這可以通過處理程序或廣播或您能想到的任何其他內容。

最簡單的方法是定義自己的異常對象,該對象擴展RuntimeException類而不是Exception類。

暫無
暫無

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

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