2012-07-20 28 views
51

Metoda, którą wywołuję w run() w klasie, która implementuje Runnable) jest przeznaczona do zgłaszania wyjątku.Czy istnieje sposób, aby uruchomić Runnable() throw wyjątek?

Ale kompilator Java nie pozwoli mi tego zrobić i sugeruje, że otaczam go try/catch.

Problem polega na tym, że otaczając go próbą/catch, robię , że konkretnyrun() jest bezużyteczny. I do chcę rzucić wyjątek.

Jeśli podam samą throws dla samej run(), kompilator narzeka, że ​​Exception is not compatible with throws clause in Runnable.run().

Zazwyczaj jestem całkowicie w porządku, nie pozwalając run() rzucić wyjątku. Ale mam wyjątkową sytuację, w której muszę mieć tę funkcjonalność.

Jak obejść to ograniczenie?

+0

oprócz innych odpowiedzi, aby śledzić postęp zadania, można użyj klasy FutureTask. – JProgrammer

+0

Non-android Java pytanie: http://stackoverflow.com/questions/1369204/how-to-throw-a-checked-exception-from-a-java-thread?lq=1 –

Odpowiedz

15

Jeśli chcesz przekazać klasę, która implementuje Runnable do struktury Thread, musisz grać zgodnie z zasadami tego schematu, zobacz odpowiedź Ernesta Friedmana-Hilla, dlaczego robienie tego w inny sposób jest złym pomysłem.

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

Odpowiedź na ten problem jest prosta. Nie używaj interfejsu Runnable z biblioteki Thread, ale zamiast tego twórz własny interfejs ze zmodyfikowanym sygnaturą, który umożliwia zgłaszanie sprawdzonego wyjątku, np.

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

Można nawet stworzyć adapter, który przekształca ten interfejs do realnego Runnable (sprawdzane przez obsługę wyjątku) nadaje się do stosowania w ramach wątku.

+22

W jaki sposób wątek korzysta z tego interfejsu? –

+0

Takie proste rozwiązanie pojawiło się po prostu nie myśląc "w pudełku". Oczywiście, 'Runnable' to po prostu prosty interfejs i możemy stworzyć własny. Nie przydatne w przypadku użycia wątku, ale do przekazywania różnych "działających" fragmentów kodu wokół tego jest doskonały. –

16

Jeśli run() rzucił sprawdzony wyjątek, co by go złapało? Nie ma sposobu, aby zamknąć to wywołanie run() w programie obsługi, ponieważ nie zapisujesz kodu, który go wywołuje.

Możesz przechwycić sprawdzony wyjątek w metodzie run() i zamiast tego umieścić wyjątek (np. RuntimeException) w tym miejscu. Spowoduje to zakończenie wątku za pomocą śledzenia stosu; być może właśnie o to prosisz.

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

-1

Twoje wymagania nie mają żadnego sensu. Jeśli chcesz powiadomić o wywołanym wątku o wyjątku, który się zdarzył, możesz to zrobić za pomocą mechanizmu oddzwaniania. Może to być przez Handler lub transmisję lub cokolwiek innego, co możesz wymyślić.

0

Myślę, że listener pattern może ci pomóc w tym scenariuszu. W przypadku wyjątku występującego w Twojej metodzie run() użyj bloku try-catch iw haczyku wyślij powiadomienie o zdarzeniu wyjątku. A następnie zajmij się wydarzeniem z powiadomieniem. Myślę, że byłoby to czystsze podejście. This SO link podaje pomocną wskazówkę w tym kierunku.

59

Można użyć Callable zamiast złożenie go do ExecutorService i czeka na wynik z FutureTask.isDone() zwracanej przez ExecutorService.submit().

Gdy isDone() zwraca wartość true, należy wywołać FutureTask.get(). Teraz, jeśli Twój Callable rzucił Exception, a następnie FutureTask.get(), wyrzuci również Exception i oryginalny wyjątek, będziesz mógł uzyskać dostęp za pomocą Exception.getCause().

+5

Chciałbym zobaczyć przykład kodu – gromit190

13

Tak, istnieje sposób, aby rzucić wyjątek sprawdzony z metody run(), ale jest tak straszne, że nie będę go udostępniać.

Oto, co możesz zrobić; wykorzystuje ten sam mechanizm, który będzie się wyjątek czasu wykonywania ćwiczenia:

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

jak inni to zauważyli, jeśli metoda run() jest naprawdę cel z Thread, nie ma sensu rzucać wyjątek, ponieważ jest niedostrzegalna; zgłoszenie wyjątku ma taki sam efekt, jak nie zgłoszenie wyjątku (brak).

Jeśli nie jest to wartość docelowa Thread, nie należy używać wartości Runnable. Na przykład prawdopodobnie lepiej pasuje Callable.

+0

Ale czy to powoduje awarię procesu, kiedy rzuca? –

+0

@ DineshVG Nie, tylko błąd w maszynie JVM może spowodować prawdziwą awarię. Domyślna procedura obsługi wyjątku wypisuje wyjątek. Jeśli jesteś przyzwyczajony do wyświetlenia wyjścia procesu po tym, to dlatego, że wątek był jedynym wątkiem działającym i został zakończony. – erickson

+0

Próbowałem (w przypadku testów oprzyrządowania Android), używając tego do wywołania mydła, gdzie jeśli otrzymam 400 od wywołania Soap, zgłaszam wyjątek. To wywołanie mydła jest wywoływane z wątku podczas uruchamiania przypadku testowego. Ten wątek używa tego 't.getUncaughtExceptionHandler(). UncaughtException (t, ex);' do wyrzucenia go do przypadku testowego oprzyrządowania. Dodanie tej jednej linii powoduje awarię procesu !. Nie wiem dlaczego. –

-1

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

+0

A jak można uzyskać ten wyjątek RuntimeException, gdy wystąpi mój drogi Sir? – Dormouse

+0

Samo to nie odpowiada na pytanie. –

0

Oto jak można owinąć klasę ze sprawdzonych wyjątków i nadal być w stanie pokryć/przetestować całą klasę

public final class Json { 

private Json() { 
} 

public static ObjectMapper standard() { 
    final ObjectMapper objectMapper = new ObjectMapper(); 
    objectMapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES); 
    objectMapper.enable(DeserializationFeature.READ_UNKNOWN_ENUM_VALUES_AS_NULL); 
    objectMapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS); 
    objectMapper.setDefaultSetterInfo(JsonSetter.Value.construct(Nulls.AS_EMPTY, Nulls.AS_EMPTY)); 
    objectMapper.findAndRegisterModules(); 
    return objectMapper; 
} 

public static String writeValueAsString(final Object value) { 
    return transformException(() -> standard().writeValueAsString(value)); 
} 

public static String writeValueAsStringPretty(final Object value) { 
    return transformException(() -> standard().writerWithDefaultPrettyPrinter().writeValueAsString(value)); 
} 

static String transformException(final Callable<String> action) { 
    try { 
     return action.call(); 
    } catch (Exception e) { 
     throw new IllegalStateException(e); 
    } 
} 

}