Metoda, którą wywołuję w run () w klasie, która implementuje Runnable ) jest przeznaczona do zgłaszania wyjątku.
Ale kompilator Java nie pozwala mi tego zrobić i sugeruje, żebym otoczył go try / catch.
Problem polega na tym, że otaczając go próbą / złapaniem, sprawiam, że ten konkretny run () staje się bezużyteczny. I nie chcą rzucać ten wyjątek.
Jeśli podam throws
dla samego run () , kompilator narzeka Exception is not compatible with throws clause in Runnable.run()
.
Zwykle jestem całkowicie w porządku, nie pozwalając run () rzucać wyjątku. Ale mam wyjątkową sytuację, w której muszę mieć taką funkcjonalność.
Jak obejść to ograniczenie?
Odpowiedzi:
Jeśli chcesz przekazać klasę, która implementuje
Runnable
się doThread
frameworka, musisz postępować zgodnie z regułami tego frameworka, zobacz odpowiedź Ernesta Friedmana-Hilla, dlaczego zrobienie tego w inny sposób jest złym pomysłem.Mam jednak przeczucie, że chcesz wywołać
run
metodę bezpośrednio w kodzie, aby kod wywołujący mógł przetworzyć wyjątek.Odpowiedź na ten problem jest prosta. Nie używaj
Runnable
interfejsu z biblioteki Thread, ale stwórz własny interfejs ze zmodyfikowaną sygnaturą, która pozwala na wyrzucenie zaznaczonego wyjątku, np.public interface MyRunnable { void myRun ( ) throws MyException; }
Możesz nawet utworzyć adapter, który konwertuje ten interfejs na rzeczywisty
Runnable
(poprzez obsługę zaznaczonego wyjątku) odpowiedni do użycia w środowisku Thread.źródło
Runnable
to tylko prosty interfejs i możemy stworzyć własny. Nie jest to przydatne w przypadku użycia wątku, ale do przekazywania różnych „uruchamialnych” fragmentów kodu wokół tego jest idealne.Możesz użyć
Callable
zamiast tego, przesyłając go do anExecutorService
i czekając na wynik zFutureTask.isDone()
zwróconym przezExecutorService.submit()
.Kiedy
isDone()
zwraca prawdę, dzwoniszFutureTask.get()
. Teraz, jeśliCallable
wyrzuciłeś, aException
następnieFutureTask.get()
wyrzuciszException
również, a oryginalny Wyjątek będziesz mógł uzyskać dostęp za pomocąException.getCause()
.źródło
run()
Co by go złapało, gdyby rzucił zaznaczony wyjątek? Nie ma sposobu, aby zawrzeć torun()
wywołanie w module obsługi, ponieważ nie piszesz kodu, który je wywołuje.Możesz złapać sprawdzony wyjątek w
run()
metodzie iRuntimeException
w jego miejsce rzucić niezatwierdzony wyjątek (tj. ). Spowoduje to zakończenie wątku ze śladem stosu; może o to ci chodzi.Jeśli zamiast tego chcesz, aby Twoja
run()
metoda zgłosiła gdzieś błąd, możesz po prostu podać metodę wywołania zwrotnego do wywołania blokurun()
metodycatch
; ta metoda mogłaby gdzieś przechowywać obiekt wyjątku, a następnie zainteresowany wątek mógłby znaleźć obiekt w tej lokalizacji.źródło
main()
zgłosiłby wyjątek zaznaczony, co by go złapało?” „Gdybyrun()
wyrzucił niesprawdzony wyjątek, co by go złapało?”Tak, istnieje sposób na rzucenie zaznaczonego wyjątku od
run()
metody, ale jest tak okropny, że nie będę go udostępniać.Oto, co możesz zrobić zamiast tego; używa tego samego mechanizmu, co wyjątek czasu wykonania:
@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
run()
metoda jest naprawdę celem aThread
, nie ma sensu rzucać wyjątku, ponieważ jest nieobserwowalny; zgłoszenie wyjątku ma taki sam efekt, jak nie zgłoszenie wyjątku (brak).Jeśli to nie jest
Thread
cel, nie używajRunnable
. Na przykład możeCallable
lepiej pasuje.źródło
t.getUncaughtExceptionHandler().uncaughtException(t, ex);
aby przesłać go do przypadku testowego oprzyrządowania. Dodanie tej jednej linii powoduje awarię procesu !. Nie wiem dlaczego.Thread.getDefaultUncaughtExceptionHandler()
powracanull
? Jeśli nie, jaki jest typ wyniku? Co się stanie, jeśli zamiast wywoływaćuncaughtException()
, zawiniesz zaznaczony wyjątek w aRuntimeException
i wyrzucisz to?Thread.getDefaultUncaughtExceptionHandler()
wracanull
; jeśli nie, Android zapewnia ustawienie domyślne, aby zaoferować raportowanie itp. Ale możesz ustawić to, co chcesz. Więcej informacji znajdziesz tutaj.@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; }
źródło
Niektórzy próbują cię przekonać, że musisz grać według zasad. Posłuchaj, ale czy jesteś posłuszny, powinieneś zdecydować sam w zależności od sytuacji. Rzeczywistość jest taka, że „POWINIENEŚ grać według zasad” (a nie „MUSISZ grać według zasad”). Pamiętaj tylko, że jeśli nie będziesz postępować zgodnie z zasadami, może to mieć konsekwencje.
Sytuacja dotyczy nie tylko sytuacji
Runnable
, ale z Javą 8 również bardzo często w kontekście Strumieni i innych miejsc, w których wprowadzono funkcjonalne interfejsy bez możliwości radzenia sobie z zaznaczonymi wyjątkami. Na przykładConsumer
,Supplier
,Function
,BiFunction
i tak dalej, wszystkie zostały uznane bez urządzeń do czynienia ze sprawdzonych wyjątków.Więc jakie są sytuacje i opcje? W poniższym tekście
Runnable
jest reprezentatywny dla każdego funkcjonalnego interfejsu, który nie deklaruje wyjątków lub deklaruje wyjątki zbyt ograniczone do danego przypadku użycia.Runnable
Sam się gdzieś zadeklarowałeś i możesz zamienić naRunnable
coś innego.Runnable
zCallable<Void>
. Zasadniczo to samo, ale można zgłaszać wyjątki; i musireturn null
w końcu, co jest łagodną irytacją.Runnable
własnym niestandardowym,@FunctionalInterface
który może generować dokładnie te wyjątki, które chcesz.Callable<Void>
zamiastRunnable
.RuntimeException
.Możesz spróbować następujących rzeczy. To trochę hackowanie, ale czasami potrzebujemy włamania. Ponieważ to, czy wyjątek powinien być zaznaczony, czy odznaczony, jest określane przez jego typ, ale praktycznie powinno być definiowane 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 bardziej,
new RuntimeException(t)
ponieważ ma krótszy ślad stosu.Możesz teraz:
executorService.submit((ThrowingRunnable) () -> {throw new Exception()});
Zastrzeżenie: Możliwość wykonywania niesprawdzonych rzutów w ten sposób może w rzeczywistości zostać usunięta w przyszłych wersjach języka Java, gdy informacje o typach ogólnych są przetwarzane nie tylko w czasie kompilacji, ale także w czasie wykonywania.
źródło
Twoje wymaganie nie ma sensu. Jeśli chcesz powiadomić wywoływanego wątku o zdarzeniu wyjątku, możesz to zrobić za pomocą mechanizmu wywołania zwrotnego. Może to nastąpić za pośrednictwem Handlera, transmisji lub czegokolwiek innego, o czym możesz pomyśleć.
źródło
Myślę, że wzorzec słuchacza może ci pomóc w tym scenariuszu. W przypadku wystąpienia wyjątku w Twojej
run()
metodzie, użyj bloku try-catch iw catch wyślij powiadomienie o zdarzeniu wyjątku. A następnie obsłuż zdarzenie powiadomienia. Myślę, że byłoby to czystsze podejście. Ten odsyłacz SO zawiera pomocne wskazówki w tym kierunku.źródło
Najłatwiejszym sposobem jest zdefiniowanie własnego obiektu wyjątku, który rozszerza
RuntimeException
klasę zamiastException
klasy.źródło