Jak mogę wyrzucać zaznaczone wyjątki ze strumieni Java 8?
Jak mogę wyrzucić zaznaczone wyjątki z wewnątrz Java 8 streams / lambda?
Innymi słowy, chcę stworzyć taki kod kompilacji:
public List<Class> getClasses() throws ClassNotFoundException {
List<Class> classes =
Stream.of("java.lang.Object", "java.lang.Integer", "java.lang.String")
.map(className -> Class.forName(className))
.collect(Collectors.toList());
return classes;
}
Ten kod nie jest kompilowany, ponieważ metoda Class.forName()
powyżej rzuca ClassNotFoundException
, która jest sprawdzana.
Proszę zauważyć, że nie chcę zawijać zaznaczonego wyjątku wewnątrz wyjątku uruchomieniowego i zamiast tego rzucać zawinięty, niezaznaczony wyjątek. chcę rzucić sam zaznaczony wyjątek i bez dodawania brzydkich try
/catches
na strumień.
15 answers
Prosta odpowiedź na twoje pytanie brzmi: nie możesz, przynajmniej nie bezpośrednio.I to nie twoja wina. / Align = "left" / trzymają się pojęcia sprawdzonych wyjątków, ale niekonsekwentnie zapomnieli o sprawdzonych wyjątkach przy projektowaniu funkcjonalnych interfejsów, strumieni, lambda itp. To wszystko dla takich ekspertów jak Robert C. Martin, którzy nazywają sprawdzone wyjątki nieudanym eksperymentem.
To rzeczywiście jest ogromny błąd w API i drobny błąd w specyfikacji języka .
Błąd w API polega na tym, że nie zapewnia on możliwości przekazywania sprawdzonych WYJĄTKÓW, gdzie w rzeczywistości miałoby to ogromny sens dla programowania funkcyjnego. Jak zademonstruję poniżej, taki obiekt byłby łatwo możliwy.
Błąd w specyfikacji języka polega na tym, że parametr type nie pozwala na wnioskowanie listy typów zamiast pojedynczego typu tak długo, jak typ parametr jest używany tylko w sytuacjach, w których lista typów jest dopuszczalna (klauzulathrows
).
Jako programiści Javy oczekujemy, że następujący kod powinien się skompilować:
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Stream;
public class CheckedStream {
// List variant to demonstrate what we actually had before refactoring.
public List<Class> getClasses(final List<String> names) throws ClassNotFoundException {
final List<Class> classes = new ArrayList<>();
for (final String name : names)
classes.add(Class.forName(name));
return classes;
}
// The Stream function which we want to compile.
public Stream<Class> getClasses(final Stream<String> names) throws ClassNotFoundException {
return names.map(Class::forName);
}
}
Daje jednak:
cher@armor1:~/playground/Java/checkedStream$ javac CheckedStream.java
CheckedStream.java:13: error: incompatible thrown types ClassNotFoundException in method reference
return names.map(Class::forName);
^
1 error
Sposób definiowania interfejsów funkcyjnych obecnie uniemożliwia kompilatorowi przekazanie wyjątku - nie ma deklaracji, która mówiłaby Stream.map()
, że jeśli Function.apply() throws E
, Stream.map() throws E
też.
Brakuje deklaracji typu parametr do przekazywania sprawdzonych WYJĄTKÓW. Poniższy kod pokazuje, w jaki sposób taki parametr typu pass-through mógł zostać zadeklarowany przy bieżącej składni. Z wyjątkiem specjalnego przypadku w zaznaczonej linii, który jest limitem omówionym poniżej, kod ten kompiluje i zachowuje się zgodnie z oczekiwaniami.
import java.io.IOException;
interface Function<T, R, E extends Throwable> {
// Declare you throw E, whatever that is.
R apply(T t) throws E;
}
interface Stream<T> {
// Pass through E, whatever mapper defined for E.
<R, E extends Throwable> Stream<R> map(Function<? super T, ? extends R, E> mapper) throws E;
}
class Main {
public static void main(final String... args) throws ClassNotFoundException {
final Stream<String> s = null;
// Works: E is ClassNotFoundException.
s.map(Class::forName);
// Works: E is RuntimeException (probably).
s.map(Main::convertClass);
// Works: E is ClassNotFoundException.
s.map(Main::throwSome);
// Doesn't work: E is Exception.
s.map(Main::throwSomeMore); // error: unreported exception Exception; must be caught or declared to be thrown
}
public static Class convertClass(final String s) {
return Main.class;
}
static class FooException extends ClassNotFoundException {}
static class BarException extends ClassNotFoundException {}
public static Class throwSome(final String s) throws FooException, BarException {
throw new FooException();
}
public static Class throwSomeMore(final String s) throws ClassNotFoundException, IOException {
throw new FooException();
}
}
W przypadku throwSomeMore
chcielibyśmy, aby IOException
było chybione, ale tak naprawdę chybione Exception
.
To nie jest idealne, ponieważ wnioskowanie typu wydaje się szukać pojedynczy typ, nawet w przypadku WYJĄTKÓW. Ponieważ wnioskowanie typu wymaga pojedynczego typu, E
musi rozstrzygnąć do wspólnego super
z ClassNotFoundException
i IOException
, CZYLI Exception
.
Konieczne jest dostosowanie definicji wnioskowania typu, aby kompilator szukał wielu typów, jeśli parametr typu jest używany, gdy dopuszczalna jest lista typów (klauzulathrows
). Wtedy typ wyjątku zgłaszany przez kompilator będzie tak specyficzny jak oryginalna deklaracja throws
sprawdzonego wyjątki metody odniesienia, ani jednego typu super catch-all.
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
2014-12-27 14:49:59
Ta klasa pomocnicza LambdaExceptionUtil
pozwala na użycie dowolnych zaznaczonych WYJĄTKÓW w strumieniach Javy, takich jak:
Stream.of("java.lang.Object", "java.lang.Integer", "java.lang.String")
.map(rethrowFunction(Class::forName))
.collect(Collectors.toList());
Uwaga Class::forName
rzuca ClassNotFoundException
, która jest sprawdzona . Sam strumień również rzuca ClassNotFoundException
, a nie jakiś wyjątek zawijania.
public final class LambdaExceptionUtil {
@FunctionalInterface
public interface Consumer_WithExceptions<T, E extends Exception> {
void accept(T t) throws E;
}
@FunctionalInterface
public interface BiConsumer_WithExceptions<T, U, E extends Exception> {
void accept(T t, U u) throws E;
}
@FunctionalInterface
public interface Function_WithExceptions<T, R, E extends Exception> {
R apply(T t) throws E;
}
@FunctionalInterface
public interface Supplier_WithExceptions<T, E extends Exception> {
T get() throws E;
}
@FunctionalInterface
public interface Runnable_WithExceptions<E extends Exception> {
void run() throws E;
}
/** .forEach(rethrowConsumer(name -> System.out.println(Class.forName(name)))); or .forEach(rethrowConsumer(ClassNameUtil::println)); */
public static <T, E extends Exception> Consumer<T> rethrowConsumer(Consumer_WithExceptions<T, E> consumer) throws E {
return t -> {
try { consumer.accept(t); }
catch (Exception exception) { throwAsUnchecked(exception); }
};
}
public static <T, U, E extends Exception> BiConsumer<T, U> rethrowBiConsumer(BiConsumer_WithExceptions<T, U, E> biConsumer) throws E {
return (t, u) -> {
try { biConsumer.accept(t, u); }
catch (Exception exception) { throwAsUnchecked(exception); }
};
}
/** .map(rethrowFunction(name -> Class.forName(name))) or .map(rethrowFunction(Class::forName)) */
public static <T, R, E extends Exception> Function<T, R> rethrowFunction(Function_WithExceptions<T, R, E> function) throws E {
return t -> {
try { return function.apply(t); }
catch (Exception exception) { throwAsUnchecked(exception); return null; }
};
}
/** rethrowSupplier(() -> new StringJoiner(new String(new byte[]{77, 97, 114, 107}, "UTF-8"))), */
public static <T, E extends Exception> Supplier<T> rethrowSupplier(Supplier_WithExceptions<T, E> function) throws E {
return () -> {
try { return function.get(); }
catch (Exception exception) { throwAsUnchecked(exception); return null; }
};
}
/** uncheck(() -> Class.forName("xxx")); */
public static void uncheck(Runnable_WithExceptions t)
{
try { t.run(); }
catch (Exception exception) { throwAsUnchecked(exception); }
}
/** uncheck(() -> Class.forName("xxx")); */
public static <R, E extends Exception> R uncheck(Supplier_WithExceptions<R, E> supplier)
{
try { return supplier.get(); }
catch (Exception exception) { throwAsUnchecked(exception); return null; }
}
/** uncheck(Class::forName, "xxx"); */
public static <T, R, E extends Exception> R uncheck(Function_WithExceptions<T, R, E> function, T t) {
try { return function.apply(t); }
catch (Exception exception) { throwAsUnchecked(exception); return null; }
}
@SuppressWarnings ("unchecked")
private static <E extends Throwable> void throwAsUnchecked(Exception exception) throws E { throw (E)exception; }
}
Wiele innych przykładów jak go używać (po zaimportowaniu statycznie LambdaExceptionUtil
):
@Test
public void test_Consumer_with_checked_exceptions() throws IllegalAccessException {
Stream.of("java.lang.Object", "java.lang.Integer", "java.lang.String")
.forEach(rethrowConsumer(className -> System.out.println(Class.forName(className))));
Stream.of("java.lang.Object", "java.lang.Integer", "java.lang.String")
.forEach(rethrowConsumer(System.out::println));
}
@Test
public void test_Function_with_checked_exceptions() throws ClassNotFoundException {
List<Class> classes1
= Stream.of("Object", "Integer", "String")
.map(rethrowFunction(className -> Class.forName("java.lang." + className)))
.collect(Collectors.toList());
List<Class> classes2
= Stream.of("java.lang.Object", "java.lang.Integer", "java.lang.String")
.map(rethrowFunction(Class::forName))
.collect(Collectors.toList());
}
@Test
public void test_Supplier_with_checked_exceptions() throws ClassNotFoundException {
Collector.of(
rethrowSupplier(() -> new StringJoiner(new String(new byte[]{77, 97, 114, 107}, "UTF-8"))),
StringJoiner::add, StringJoiner::merge, StringJoiner::toString);
}
@Test
public void test_uncheck_exception_thrown_by_method() {
Class clazz1 = uncheck(() -> Class.forName("java.lang.String"));
Class clazz2 = uncheck(Class::forName, "java.lang.String");
}
@Test (expected = ClassNotFoundException.class)
public void test_if_correct_exception_is_still_thrown_by_method() {
Class clazz3 = uncheck(Class::forName, "INVALID");
}
Uwaga 1: metody rethrow
klasy LambdaExceptionUtil
mogą być używane bez obaw i są W porządku do użycia w dowolnym situation . Wielkie podziękowania dla użytkownika @ PaoloC, który pomógł rozwiązać ostatni problem: teraz kompilator poprosi Cię o dodanie klauzul throw i wszystko jest tak, jakbyś mógł natywnie rzucać zaznaczone wyjątki na strumieniach Java 8.
Uwaga 2: metody uncheck
klasy powyżej są metodami bonusowymi i mogą być bezpiecznie usunięte z klasy, jeśli nie chcesz ich używać. Jeśli używasz ich, zrób to ostrożnie, a nie przed zrozumieniem następujących przypadków użycia, zalety/wady i ograniczenia:
* możesz użyć metod uncheck
, jeśli wywołujesz metodę, która dosłownie nigdy nie może wyrzucić deklarowanego wyjątku. Na przykład: new String (byteArr, "UTF-8") wyrzuca Nieobsługiwanencodingexception, ale specyfikacja Java gwarantuje, że UTF-8 zawsze będzie obecny. W tym przypadku deklaracja rzutów jest uciążliwa i mile widziane jest każde rozwiązanie, aby wyciszyć ją minimalnym kotłem: String text = uncheck(() -> new String(byteArr, "UTF-8"));
• możesz użyć metod uncheck
, jeśli jesteś zaimplementowanie ścisłego interfejsu, w którym nie ma opcji dodawania deklaracji throws, a jednak rzucenie wyjątku jest całkowicie właściwe. Owijanie wyjątku tylko po to, aby zyskać przywilej rzucania go, powoduje stacktrace z fałszywymi wyjątkami, które nie dostarczają informacji o tym, co faktycznie poszło źle. Dobrym przykładem jest Runnable.run (), która nie wyrzuca żadnych zaznaczonych WYJĄTKÓW.
• w każdym przypadku, jeśli zdecydujesz się użyć metod uncheck
,
bądź świadomy tych 2 konsekwencje wyrzucenia zaznaczonych WYJĄTKÓW bez klauzuli throws: 1) kod wywołujący nie będzie w stanie złapać go po nazwie (jeśli spróbujesz, kompilator powie: wyjątek nigdy nie jest wrzucany do odpowiedniej instrukcji try). Będzie bańka i prawdopodobnie zostanie złapany w głównej pętli programu przez jakiś "catch Exception" lub "catch Throwable" , co może być tym, czego chcesz. 2) łamie zasadę najmniejszego zaskoczenia: nie wystarczy już złapać RuntimeException
, aby móc zagwarantować połów wszystkie możliwe wyjątki. Z tego powodu uważam, że nie powinno się tego robić w kodach ramowych, ale tylko w kodach biznesowych, które całkowicie kontrolujesz.
- :
- http://www.philandstuff.com/2012/04/28/sneakily-throwing-checked-exceptions.html
- http://www.mail-archive.com/[email protected]/msg05984.html
- projekt Lombok adnotacja: @ SneakyThrows
- Brian Goetz opinia (przeciw) tutaj: Jak mogę wyrzucać zaznaczone wyjątki ze strumieni Java 8?
- https://softwareengineering.stackexchange.com/questions/225931/workaround-for-java-checked-exceptions?newreg=ddf0dd15e8174af8ba52e091cf85688e *
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-02-26 19:09:44
Nie możesz tego zrobić bezpiecznie. Możesz oszukiwać, ale wtedy twój program jest zepsuty i to nieuchronnie wróci, aby ugryźć kogoś (to powinieneś być ty, ale często nasze oszustwo wybucha na kogoś innego.)
Tutaj jest nieco bezpieczniejszy sposób, aby to zrobić (ale nadal nie polecam tego.)
class WrappedException extends RuntimeException {
Throwable cause;
WrappedException(Throwable cause) { this.cause = cause; }
}
static WrappedException throwWrapped(Throwable t) {
throw new WrappedException(t);
}
try
source.stream()
.filter(e -> { ... try { ... } catch (IOException e) { throwWrapped(e); } ... })
...
}
catch (WrappedException w) {
throw (IOException) w.cause;
}
Tutaj, to co robisz, to wyłapywanie wyjątku w lambda, wyrzucanie sygnału z rurociągu strumienia, który wskazuje, że obliczenia nie powiodły się wyjątkowo, wyłapywanie sygnału i działając na ten sygnał, aby rzucić podstawowy wyjątek. Kluczem jest to, że zawsze łapiesz syntetyczny wyjątek, zamiast zezwalać na wyciek sprawdzonego wyjątku bez deklarowania, że wyjątek został wyrzucony.
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-08-24 12:28:57
Możesz!
Rozszerzenie @marcg 's UtilException
i dodanie throw E
tam, gdzie jest to konieczne: w ten sposób kompilator poprosi Cię o dodanie klauzul throwi wszystko jest tak, jakbyś mógł rzucić zaznaczone wyjątki natywnie w strumieniach java 8.
Instrukcje: po prostu skopiuj / wklej LambdaExceptionUtil
do swojego IDE, a następnie użyj go, jak pokazano poniżej LambdaExceptionUtilTest
.
public final class LambdaExceptionUtil {
@FunctionalInterface
public interface Consumer_WithExceptions<T, E extends Exception> {
void accept(T t) throws E;
}
@FunctionalInterface
public interface Function_WithExceptions<T, R, E extends Exception> {
R apply(T t) throws E;
}
/**
* .forEach(rethrowConsumer(name -> System.out.println(Class.forName(name))));
*/
public static <T, E extends Exception> Consumer<T> rethrowConsumer(Consumer_WithExceptions<T, E> consumer) throws E {
return t -> {
try {
consumer.accept(t);
} catch (Exception exception) {
throwActualException(exception);
}
};
}
/**
* .map(rethrowFunction(name -> Class.forName(name))) or .map(rethrowFunction(Class::forName))
*/
public static <T, R, E extends Exception> Function<T, R> rethrowFunction(Function_WithExceptions<T, R, E> function) throws E {
return t -> {
try {
return function.apply(t);
} catch (Exception exception) {
throwActualException(exception);
return null;
}
};
}
@SuppressWarnings("unchecked")
private static <E extends Exception> void throwActualException(Exception exception) throws E {
throw (E) exception;
}
}
Jakiś test pokazujący użycie i zachowanie:
public class LambdaExceptionUtilTest {
@Test(expected = MyTestException.class)
public void testConsumer() throws MyTestException {
Stream.of((String)null).forEach(rethrowConsumer(s -> checkValue(s)));
}
private void checkValue(String value) throws MyTestException {
if(value==null) {
throw new MyTestException();
}
}
private class MyTestException extends Exception { }
@Test
public void testConsumerRaisingExceptionInTheMiddle() {
MyLongAccumulator accumulator = new MyLongAccumulator();
try {
Stream.of(2L, 3L, 4L, null, 5L).forEach(rethrowConsumer(s -> accumulator.add(s)));
fail();
} catch (MyTestException e) {
assertEquals(9L, accumulator.acc);
}
}
private class MyLongAccumulator {
private long acc = 0;
public void add(Long value) throws MyTestException {
if(value==null) {
throw new MyTestException();
}
acc += value;
}
}
@Test
public void testFunction() throws MyTestException {
List<Integer> sizes = Stream.of("ciao", "hello").<Integer>map(rethrowFunction(s -> transform(s))).collect(toList());
assertEquals(2, sizes.size());
assertEquals(4, sizes.get(0).intValue());
assertEquals(5, sizes.get(1).intValue());
}
private Integer transform(String value) throws MyTestException {
if(value==null) {
throw new MyTestException();
}
return value.length();
}
@Test(expected = MyTestException.class)
public void testFunctionRaisingException() throws MyTestException {
Stream.of("ciao", null, "hello").<Integer>map(rethrowFunction(s -> transform(s))).collect(toList());
}
}
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-10-03 10:22:14
Po prostu użyj jednego z NoException (Mój projekt), jooooo, rzucanie-lambda, Throwable interfaces , or Faux Pas .
// NoException
stream.map(Exceptions.sneak().function(Class::forName));
// jOOλ
stream.map(Unchecked.function(Class::forName));
// throwing-lambdas
stream.map(Throwing.function(Class::forName).sneakyThrow());
// Throwable interfaces
stream.map(FunctionWithThrowable.aFunctionThatUnsafelyThrowsUnchecked(Class::forName));
// Faux Pas
stream.map(FauxPas.throwingFunction(Class::forName));
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-06-16 03:44:51
Napisałem bibliotekę , która rozszerza API strumienia, aby umożliwić rzucanie zaznaczonych WYJĄTKÓW. Wykorzystuje sztuczkę Briana Goetza.
Twój kod stałby się
public List<Class> getClasses() throws ClassNotFoundException {
Stream<String> classNames =
Stream.of("java.lang.Object", "java.lang.Integer", "java.lang.String");
return ThrowingStream.of(classNames, ClassNotFoundException.class)
.map(Class::forName)
.collect(Collectors.toList());
}
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
2015-08-10 16:49:37
Ta odpowiedź jest podobna do 17, ale unikając definicji wyjątku wrappera:
List test = new ArrayList();
try {
test.forEach(obj -> {
//let say some functionality throws an exception
try {
throw new IOException("test");
}
catch(Exception e) {
throw new RuntimeException(e);
}
});
}
catch (RuntimeException re) {
if(re.getCause() instanceof IOException) {
//do your logic for catching checked
}
else
throw re; // it might be that there is real runtime 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-12-01 15:09:22
Nie możesz.
Warto jednak rzucić okiem na jeden z moich projektów, który pozwala łatwiej manipulować takimi "rzucającymi lambdami".
W Twoim przypadku, byłabyś w stanie to zrobić:
import static com.github.fge.lambdas.functions.Functions.wrap;
final ThrowingFunction<String, Class<?>> f = wrap(Class::forName);
List<Class> classes =
Stream.of("java.lang.Object", "java.lang.Integer", "java.lang.String")
.map(f.orThrow(MyException.class))
.collect(Collectors.toList());
I złapać MyException
.
.orReturn()
jakąś wartość domyślną.
Zauważ, że jest to nadal praca w toku, więcej ma nadejść. Lepsze nazwy, więcej funkcji itp.
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
2014-12-27 15:27:10
Podsumowując powyższe komentarze, zaawansowanym rozwiązaniem jest użycie specjalnego opakowania dla niezaznaczonych funkcji z budowniczym, takim jak API, które zapewnia odzyskiwanie, ponowne przeglądanie i tłumienie.
Stream.of("java.lang.Object", "java.lang.Integer", "java.lang.String")
.map(Try.<String, Class<?>>safe(Class::forName)
.handle(System.out::println)
.unsafe())
.collect(toList());
Poniższy kod pokazuje to dla interfejsów konsumentów, dostawców i funkcji. Można go łatwo rozszerzyć. Niektóre publiczne słowa kluczowe zostały usunięte w tym przykładzie.
Class Try jest punktem końcowym kodu klienta. Bezpieczne metody mogą mieć unikalną nazwę dla każdej funkcji Typ. CheckedConsumer, CheckedSupplier i CheckedFunction są sprawdzonymi analogami funkcji lib, które mogą być używane niezależnie od Try
CheckedBuilder {[8] } jest interfejsem do obsługi wyjątków w niektórych funkcji checked. orTry pozwala na wykonanie innej funkcji tego samego typu, jeśli poprzednia nie powiodła się. handle zapewnia obsługę wyjątków, w tym filtrowanie typów wyjątków. Kolejność obsługi jest ważna. Redukcja metod unsafe i rethrow rethrows ostatni wyjątek w łańcuchu realizacji. Metody Reduce orElse i orElseGet zwracają alternatywną wartość, jak opcjonalne, jeśli wszystkie funkcje zawiodły. Istnieje również metoda tłumić . CheckedWrapper jest powszechną implementacją CheckedBuilder.
final class Try {
public static <T> CheckedBuilder<Supplier<T>, CheckedSupplier<T>, T>
safe(CheckedSupplier<T> supplier) {
return new CheckedWrapper<>(supplier,
(current, next, handler, orResult) -> () -> {
try { return current.get(); } catch (Exception ex) {
handler.accept(ex);
return next.isPresent() ? next.get().get() : orResult.apply(ex);
}
});
}
public static <T> Supplier<T> unsafe(CheckedSupplier<T> supplier) {
return supplier;
}
public static <T> CheckedBuilder<Consumer<T>, CheckedConsumer<T>, Void>
safe(CheckedConsumer<T> consumer) {
return new CheckedWrapper<>(consumer,
(current, next, handler, orResult) -> t -> {
try { current.accept(t); } catch (Exception ex) {
handler.accept(ex);
if (next.isPresent()) {
next.get().accept(t);
} else {
orResult.apply(ex);
}
}
});
}
public static <T> Consumer<T> unsafe(CheckedConsumer<T> consumer) {
return consumer;
}
public static <T, R> CheckedBuilder<Function<T, R>, CheckedFunction<T, R>, R>
safe(CheckedFunction<T, R> function) {
return new CheckedWrapper<>(function,
(current, next, handler, orResult) -> t -> {
try { return current.applyUnsafe(t); } catch (Exception ex) {
handler.accept(ex);
return next.isPresent() ? next.get().apply(t) : orResult.apply(ex);
}
});
}
public static <T, R> Function<T, R> unsafe(CheckedFunction<T, R> function) {
return function;
}
@SuppressWarnings ("unchecked")
static <T, E extends Throwable> T throwAsUnchecked(Throwable exception) throws E {
throw (E) exception;
}
}
@FunctionalInterface interface CheckedConsumer<T> extends Consumer<T> {
void acceptUnsafe(T t) throws Exception;
@Override default void accept(T t) {
try { acceptUnsafe(t); } catch (Exception ex) {
Try.throwAsUnchecked(ex);
}
}
}
@FunctionalInterface interface CheckedFunction<T, R> extends Function<T, R> {
R applyUnsafe(T t) throws Exception;
@Override default R apply(T t) {
try { return applyUnsafe(t); } catch (Exception ex) {
return Try.throwAsUnchecked(ex);
}
}
}
@FunctionalInterface interface CheckedSupplier<T> extends Supplier<T> {
T getUnsafe() throws Exception;
@Override default T get() {
try { return getUnsafe(); } catch (Exception ex) {
return Try.throwAsUnchecked(ex);
}
}
}
interface ReduceFunction<TSafe, TUnsafe, R> {
TSafe wrap(TUnsafe current, Optional<TSafe> next,
Consumer<Throwable> handler, Function<Throwable, R> orResult);
}
interface CheckedBuilder<TSafe, TUnsafe, R> {
CheckedBuilder<TSafe, TUnsafe, R> orTry(TUnsafe next);
CheckedBuilder<TSafe, TUnsafe, R> handle(Consumer<Throwable> handler);
<E extends Throwable> CheckedBuilder<TSafe, TUnsafe, R> handle(
Class<E> exceptionType, Consumer<E> handler);
CheckedBuilder<TSafe, TUnsafe, R> handleLast(Consumer<Throwable> handler);
<E extends Throwable> CheckedBuilder<TSafe, TUnsafe, R> handleLast(
Class<E> exceptionType, Consumer<? super E> handler);
TSafe unsafe();
TSafe rethrow(Function<Throwable, Exception> transformer);
TSafe suppress();
TSafe orElse(R value);
TSafe orElseGet(Supplier<R> valueProvider);
}
final class CheckedWrapper<TSafe, TUnsafe, R>
implements CheckedBuilder<TSafe, TUnsafe, R> {
private final TUnsafe function;
private final ReduceFunction<TSafe, TUnsafe, R> reduceFunction;
private final CheckedWrapper<TSafe, TUnsafe, R> root;
private CheckedWrapper<TSafe, TUnsafe, R> next;
private Consumer<Throwable> handlers = ex -> { };
private Consumer<Throwable> lastHandlers = ex -> { };
CheckedWrapper(TUnsafe function,
ReduceFunction<TSafe, TUnsafe, R> reduceFunction) {
this.function = function;
this.reduceFunction = reduceFunction;
this.root = this;
}
private CheckedWrapper(TUnsafe function,
CheckedWrapper<TSafe, TUnsafe, R> prev) {
this.function = function;
this.reduceFunction = prev.reduceFunction;
this.root = prev.root;
prev.next = this;
}
@Override public CheckedBuilder<TSafe, TUnsafe, R> orTry(TUnsafe next) {
return new CheckedWrapper<>(next, this);
}
@Override public CheckedBuilder<TSafe, TUnsafe, R> handle(
Consumer<Throwable> handler) {
handlers = handlers.andThen(handler);
return this;
}
@Override public <E extends Throwable> CheckedBuilder<TSafe, TUnsafe, R>
handle(Class<E> exceptionType, Consumer<E> handler) {
handlers = handlers.andThen(ex -> {
if (exceptionType.isInstance(ex)) {
handler.accept(exceptionType.cast(ex));
}
});
return this;
}
@Override public CheckedBuilder<TSafe, TUnsafe, R> handleLast(
Consumer<Throwable> handler) {
lastHandlers = lastHandlers.andThen(handler);
return this;
}
@Override public <E extends Throwable> CheckedBuilder<TSafe, TUnsafe, R>
handleLast(Class<E> exceptionType, Consumer<? super E> handler) {
lastHandlers = lastHandlers.andThen(ex -> {
if (exceptionType.isInstance(ex)) {
handler.accept(exceptionType.cast(ex));
}
});
return this;
}
@Override public TSafe unsafe() {
return root.reduce(ex -> Try.throwAsUnchecked(ex));
}
@Override
public TSafe rethrow(Function<Throwable, Exception> transformer) {
return root.reduce(ex -> Try.throwAsUnchecked(transformer.apply(ex)));
}
@Override public TSafe suppress() {
return root.reduce(ex -> null);
}
@Override public TSafe orElse(R value) {
return root.reduce(ex -> value);
}
@Override public TSafe orElseGet(Supplier<R> valueProvider) {
Objects.requireNonNull(valueProvider);
return root.reduce(ex -> valueProvider.get());
}
private TSafe reduce(Function<Throwable, R> orResult) {
return reduceFunction.wrap(function,
Optional.ofNullable(next).map(p -> p.reduce(orResult)),
this::handle, orResult);
}
private void handle(Throwable ex) {
for (CheckedWrapper<TSafe, TUnsafe, R> current = this;
current != null;
current = current.next) {
current.handlers.accept(ex);
}
lastHandlers.accept(ex);
}
}
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-23 09:48:18
Używam tego rodzaju wyjątku:
public class CheckedExceptionWrapper extends RuntimeException {
...
public <T extends Exception> CheckedExceptionWrapper rethrow() throws T {
throw (T) getCause();
}
}
Będzie wymagało obsługi tych wyjątków statycznie:
void method() throws IOException, ServletException {
try {
list.stream().forEach(object -> {
...
throw new CheckedExceptionWrapper(e);
...
});
} catch (CheckedExceptionWrapper e){
e.<IOException>rethrow();
e.<ServletExcepion>rethrow();
}
}
Chociaż wyjątek i tak zostanie ponownie wyrzucony podczas pierwszego rethrow()
wywołania (oh, Java generics...), w ten sposób można uzyskać ścisłą statyczną definicję możliwych WYJĄTKÓW (wymaga zadeklarowania ich w throws
). I nie instanceof
lub coś jest potrzebne.
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-04-13 17:41:15
Zgadzam się z powyższymi komentarzami, używając Stream.map jesteś ograniczony do implementacji funkcji, która nie wyrzuca WYJĄTKÓW.
Możesz jednak utworzyć własną interfejs funkcyjny, który rzuca jak poniżej..
@FunctionalInterface
public interface UseInstance<T, X extends Throwable> {
void accept(T instance) throws X;
}
Następnie zaimplementuj go używając lambda lub referencji, jak pokazano poniżej.
import java.io.FileWriter;
import java.io.IOException;
//lambda expressions and the execute around method (EAM) pattern to
//manage resources
public class FileWriterEAM {
private final FileWriter writer;
private FileWriterEAM(final String fileName) throws IOException {
writer = new FileWriter(fileName);
}
private void close() throws IOException {
System.out.println("close called automatically...");
writer.close();
}
public void writeStuff(final String message) throws IOException {
writer.write(message);
}
//...
public static void use(final String fileName, final UseInstance<FileWriterEAM, IOException> block) throws IOException {
final FileWriterEAM writerEAM = new FileWriterEAM(fileName);
try {
block.accept(writerEAM);
} finally {
writerEAM.close();
}
}
public static void main(final String[] args) throws IOException {
FileWriterEAM.use("eam.txt", writerEAM -> writerEAM.writeStuff("sweet"));
FileWriterEAM.use("eam2.txt", writerEAM -> {
writerEAM.writeStuff("how");
writerEAM.writeStuff("sweet");
});
FileWriterEAM.use("eam3.txt", FileWriterEAM::writeIt);
}
void writeIt() throws IOException{
this.writeStuff("How ");
this.writeStuff("sweet ");
this.writeStuff("it is");
}
}
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
2015-05-30 22:45:11
Jedynym wbudowanym sposobem obsługi sprawdzonych wyjątków, które mogą być wyrzucane przez operację map
, jest zamknięcie ich wewnątrz CompletableFuture
. (An Optional
jest prostszą alternatywą, jeśli nie musisz zachować wyjątku.) Klasy te mają na celu umożliwienie reprezentowania operacji warunkowych w sposób funkcjonalny.
Wymagane jest kilka nietrywialnych metod pomocniczych, ale można dojść do kodu, który jest stosunkowo zwięzły, jednocześnie dając do zrozumienia, że wynik transmisji zależy od pomyślnego zakończenia operacji map
. Oto jak to wygląda:
CompletableFuture<List<Class<?>>> classes =
Stream.of("java.lang.String", "java.lang.Integer", "java.lang.Double")
.map(MonadUtils.applyOrDie(Class::forName))
.map(cfc -> cfc.thenApply(Class::getSuperclass))
.collect(MonadUtils.cfCollector(ArrayList::new,
List::add,
(List<Class<?>> l1, List<Class<?>> l2) -> { l1.addAll(l2); return l1; },
x -> x));
classes.thenAccept(System.out::println)
.exceptionally(t -> { System.out.println("unable to get class: " + t); return null; });
Daje to następujące wyjście:
[class java.lang.Object, class java.lang.Number, class java.lang.Number]
The applyOrDie
metoda pobiera Function
, który wyrzuca wyjątek i przekształca go w Function
, który zwraca już zakończony CompletableFuture
-- albo zakończony normalnie z wynikiem oryginalnej funkcji, albo zakończony wyjątkowo z wyrzuconym wyjątkiem.
Druga map
Operacja ilustruje, że teraz masz Stream<CompletableFuture<T>>
zamiast tylko Stream<T>
. CompletableFuture
zajmuje się wykonywaniem tej operacji tylko wtedy, gdy operacja upstream się powiodła. API sprawia, że jest to wyjaśnione, ale stosunkowo bezbolesne.
Dopóki nie dojdziesz do fazy collect
, czyli. W tym miejscu potrzebujemy dość znaczącej metody pomocniczej. Chcemy "podnieść" normalną operację zbioru (w tym przypadku toList()
) "wewnątrz"CompletableFuture
-- cfCollector()
pozwala nam to zrobić za pomocą supplier
, accumulator
, combiner
, oraz finisher
, które nie muszą wiedzieć nic o CompletableFuture
.
Metody pomocnicze można znaleźć na Githubie w moim MonadUtils
klasy, która jest nadal w toku.
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-01 01:27:09
Myślę, że to podejście jest właściwe:
public List<Class> getClasses() throws ClassNotFoundException {
List<Class> classes;
try {
classes = Stream.of("java.lang.Object", "java.lang.Integer", "java.lang.String").map(className -> {
try {
return Class.forName(className);
} catch (ClassNotFoundException e) {
throw new UndeclaredThrowableException(e);
}
}).collect(Collectors.toList());
} catch (UndeclaredThrowableException e) {
if (e.getCause() instanceof ClassNotFoundException) {
throw (ClassNotFoundException) e.getCause();
} else {
// this should never happen
throw new IllegalStateException(e.getMessage(), e);
}
}
return classes;
}
Zawijanie wyjątku sprawdzonego wewnątrz Callable
w UndeclaredThrowableException
(to jest przypadek użycia tego wyjątku) i rozpakowywanie go na zewnątrz.
Tak, uważam to za brzydkie, i odradzam używanie lambda w tym przypadku i po prostu wycofuję się do starej, dobrej pętli, chyba że pracujesz z równoległym strumieniem i paralelizacja przynosi obiektywne korzyści, które uzasadniają nieczytelność kodu.
Jak wielu innych wskazało istnieją rozwiązania tej sytuacji i mam nadzieję, że jedno z nich przerodzi się w przyszłą wersję Javy.
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-01-24 08:10:20
Prawdopodobnie lepszym i bardziej funkcjonalnym sposobem jest owijanie wyjątków i rozprzestrzenianie ich dalej w strumieniu. Spójrz na przykład na typ Try Vavr .
Przykład:
interface CheckedFunction<I, O> {
O apply(I i) throws Exception; }
static <I, O> Function<I, O> unchecked(CheckedFunction<I, O> f) {
return i -> {
try {
return f.apply(i);
} catch(Exception ex) {
throw new RuntimeException(ex);
}
} }
fileNamesToRead.map(unchecked(file -> Files.readAllLines(file)))
Lub
@SuppressWarnings("unchecked")
private static <T, E extends Exception> T throwUnchecked(Exception e) throws E {
throw (E) e;
}
static <I, O> Function<I, O> unchecked(CheckedFunction<I, O> f) {
return arg -> {
try {
return f.apply(arg);
} catch(Exception ex) {
return throwUnchecked(ex);
}
};
}
Druga implementacja pozwala uniknąć zawijania wyjątku w RuntimeException
. throwUnchecked
działa, ponieważ prawie zawsze wszystkie ogólne wyjątki są traktowane jako niezaznaczone w Javie.
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-06-18 08:56:57
TL; DR just use Lombok ' s @SneakyThrows
.
Christian Hujer wyjaśnił już szczegółowo, dlaczego wyrzucanie sprawdzonych WYJĄTKÓW ze strumienia jest, ściśle mówiąc, niemożliwe ze względu na ograniczenia Javy.
Niektóre inne odpowiedzi wyjaśniły sztuczki, aby ominąć ograniczenia języka, ale nadal są w stanie spełnić wymóg rzucania "samego sprawdzonego wyjątku i bez dodawania brzydkich prób / łapek do strumienia" , niektóre z nich wymagają dziesiątek dodatkowych linii kotła.
Podkreślę jeszcze jedną opcję, że IMHO jest o wiele czystsza od wszystkich innych: Lombok ' s @SneakyThrows
. Zostało wspomniane przez inne odpowiedzi, ale zostało trochę zakopane pod wieloma niepotrzebnymi szczegółami.
Otrzymany kod jest tak prosty jak:
public List<Class> getClasses() throws ClassNotFoundException {
List<Class> classes =
Stream.of("java.lang.Object", "java.lang.Integer", "java.lang.String")
.map(className -> getClass(className))
.collect(Collectors.toList());
return classes;
}
@SneakyThrows // <= this is the only new code
private Class<?> getClass(String className) {
return Class.forName(className);
}
Potrzebowaliśmy tylko jednej Extract Method
refaktoryzacji (wykonanej przez IDE) i jednej dodatkowej linii dla @SneakyThrows
. Adnotacja dba o dodanie wszystkich kotłów do upewnij się, że możesz rzucić swój wyjątek checked bez zawijania go w RuntimeException
i bez konieczności deklarowania go jawnie.
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-10-09 12:12:45