Czy istnieje sposób, aby Run () Runnable ' s wyrzucił wyjątek?

Metoda, którą wywołuję w run() w klasie implementującej Runnable) została zaprojektowana tak, aby rzucać wyjątek.

Ale kompilator Javy mi na to nie pozwala i sugeruje, żebym otoczył go try/catch.

Problem polega na tym, że otaczając go próbą / łapaniem dokonuję tego konkretnego run () bezużyteczny. I do chcę rzucić ten wyjątek.

Jeśli podam throwsdla run () , kompilator narzeka, że Exception is not compatible with throws clause in Runnable.run().

Zazwyczaj nie mam nic przeciwko temu, żebyrun () wyrzucił wyjątek. Ale mam wyjątkową sytuację, w której muszę mieć tę funkcjonalność.

Jak obejść to ograniczenie?

Author: Regex Rookie, 2012-07-20

9 answers

Jeśli chcesz przekazać klasę, która implementuje Runnable do frameworka Thread, Musisz grać zgodnie z zasadami tego frameworka, zobacz odpowiedź Ernesta Friedmana-Hilla, dlaczego robienie tego inaczej jest złym pomysłem.

Mam przeczucie, że chcesz wywołać metodę run bezpośrednio w swoim kodzie, aby Twój kod mógł przetworzyć wyjątek.

Odpowiedź na ten problem jest łatwa. Nie używaj interfejsu Runnable z biblioteki wątków, ale stwórz własny interfejs ze zmodyfikowanym sygnatura pozwalająca na wyrzucenie zaznaczonego wyjątku, np.

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

Można nawet utworzyć adapter, który konwertuje ten interfejs do rzeczywistego Runnable (poprzez obsługę wyjątku checked ) odpowiedniego do użycia w ramach wątku.

 28
Author: Alexander Pogrebnyak,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-07-20 17:36:42

Możesz zamiast tego użyć Callable, przesyłając go do ExecutorService i czekając na wynik z FutureTask.isDone() zwróconym przez ExecutorService.submit().

Kiedy isDone() zwraca true, wywołujesz FutureTask.get(). Jeśli twój Callable wyrzucił Exception, to FutureTask.get() rzuci również Exception, a oryginalny wyjątek będzie dostępny za pomocą Exception.getCause().

 83
Author: Alexander Kulyakhtin,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-07-20 17:32:37

Jeśli run() rzucił zaznaczony wyjątek, co by go złapało? Nie ma sposobu, aby załączyć to run() wywołanie w handlerze, ponieważ nie piszesz kodu, który go wywołuje.

Można wyłapać zaznaczony wyjątek za pomocą metody run() i wrzucić na jego miejsce nieobsługiwany wyjątek (np. RuntimeException). Spowoduje to zakończenie wątku ze śladem stosu; być może o to ci chodzi.

Jeśli zamiast tego chcesz, aby Twoja metoda run() gdzieś zgłosiła błąd, możesz po prostu podać metoda wywołania zwrotnego dla bloku run() metody catch do wywołania; ta metoda może przechowywać obiekt wyjątku gdzieś, a następnie zainteresowany wątek może znaleźć obiekt w tej lokalizacji.

 23
Author: Ernest Friedman-Hill,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-07-20 17:28:41

Tak, jest sposób na rzuceniesprawdzonego wyjątku od metody run(), ale jest to tak straszne, że nie podzielę się nim.

Oto, co możesz zrobić zamiast tego; używa tego samego mechanizmu, który wykonywałby wyjątek runtime:

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

Jak zauważyli inni, jeśli twoja metoda run() jest naprawdę celem Thread, nie ma sensu rzucać wyjątku, ponieważ jest on nieobserwowalny; rzucanie wyjątku ma taki sam efekt, jak nie rzucanie wyjątku (brak).

Jeśli nie jest to cel Thread, nie używaj Runnable. Na przykład, być może Callable lepiej pasuje.

 18
Author: erickson,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-07-20 17:34:44
@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;

}
 6
Author: Sina Madani,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-11-20 17:07:13

Niektórzy próbują cię przekonać, że musisz grać według zasad. Słuchaj, ale czy jesteś posłuszny, powinieneś sam decydować w zależności od twojej sytuacji. Rzeczywistość brzmi "powinieneś grać według zasad "(nie "musisz grać według zasad"). Pamiętaj tylko, że jeśli nie będziesz grać zgodnie z zasadami, mogą wystąpić konsekwencje.

Sytuacja ta ma zastosowanie nie tylko w sytuacji Runnable, ale w przypadku Javy 8 również bardzo często w kontekście strumieni i innych miejsc, w których funkcjonalne interfejsy zostały wprowadzone bez możliwości radzenia sobie z zaznaczonymi wyjątkami. Na przykład, Consumer, Supplier, Function, BiFunction i tak dalej wszystkie zostały zadeklarowane bez możliwości radzenia sobie z sprawdzonymi wyjątkami.

Więc jakie są sytuacje i opcje? W poniższym tekście Runnable jest reprezentantem dowolnego interfejsu funkcjonalnego, który nie deklaruje wyjątków lub deklaruje wyjątki zbyt ograniczone dla danego przypadku użycia.

  1. zadeklarowałeś Runnable gdzieś siebie i może zastąpić Runnable czymś innym.
    1. rozważ zastąpienie Runnable przez Callable<Void>. W zasadzie to samo, ale wolno rzucać wyjątki; i musi return null W końcu, co jest lekką irytacją.
    2. rozważ zastąpienie Runnable własnym niestandardowym @FunctionalInterface, który może rzucać dokładnie te wyjątki, które chcesz.
  2. użyłeś API i dostępne są alternatywy. Na przykład niektóre API Javy są przeciążone, więc można użyć Callable<Void> zamiast Runnable.
  3. użyłeś API i nie ma alternatyw. W takim razie nadal nie masz wyboru.
    1. możesz zawinąć wyjątek w RuntimeException.
    2. możesz zhakować wyjątek do RuntimeException, używając niezaznaczonej obsady.

Możesz spróbować następujących. To trochę włamanie, ale czasami włamanie jest tym, czego potrzebujemy. Ponieważ to, czy wyjątek powinien być zaznaczony, czy nie, jest zdefiniowane przez jego typ, ale w praktyce powinno być określone przez sytuację.

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

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

    void tryRun() throws Throwable;
}

Wolę to niż new RuntimeException(t) ponieważ ma krótszy ślad stosu.

Możesz teraz zrobić:

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

Disclaimer: możliwość wykonywania niezaznaczonych odlewów w ten sposób może być faktycznie usunięta w przyszłych wersjach Javy, gdy informacje o typach generycznych są przetwarzane nie tylko w czasie kompilacji, ale także w czasie wykonywania.

 1
Author: Christian Hujer,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2019-09-17 06:14:56

Twoje wymagania nie mają sensu. Jeśli chcesz powiadomić wywołanego wątku o wystąpieniu wyjątku, możesz to zrobić za pomocą mechanizmu oddzwaniania. To może być przez opiekuna lub transmisji lub cokolwiek innego można myśleć.

 0
Author: Kumar Bibek,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-07-20 17:29:22
 0
Author: Sujay,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 11:55:07

Najprostszym sposobem jest zdefiniowanie własnego obiektu wyjątku, który rozszerza klasę RuntimeException zamiast klasy Exception.

 -1
Author: joas,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-03-15 03:02:25