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 throws
dla 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?
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.
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()
.
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.
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.
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;
}
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.
- zadeklarowałeś
Runnable
gdzieś siebie i może zastąpićRunnable
czymś innym.- rozważ zastąpienie
Runnable
przezCallable<Void>
. W zasadzie to samo, ale wolno rzucać wyjątki; i musireturn null
W końcu, co jest lekką irytacją. - rozważ zastąpienie
Runnable
własnym niestandardowym@FunctionalInterface
, który może rzucać dokładnie te wyjątki, które chcesz.
- rozważ zastąpienie
- 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>
zamiastRunnable
. - użyłeś API i nie ma alternatyw. W takim razie nadal nie masz wyboru.
- możesz zawinąć wyjątek w
RuntimeException
. - możesz zhakować wyjątek do RuntimeException, używając niezaznaczonej obsady.
- możesz zawinąć wyjątek w
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.
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ć.
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
Myślę, że wzorzec słuchacza może Ci pomóc w tym scenariuszu. W przypadku wystąpienia wyjątku w Twojej metodzie run()
, użyj bloku try-catch i Wyślij powiadomienie o zdarzeniu wyjątku. A następnie obsłuż zdarzenie powiadomienia. Myślę, że to byłoby czystsze podejście. ten link SO daje pomocną wskazówkę w tym kierunku.
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
.
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