Дуже ціную нові функції Java 8 щодо інтерфейсів лямбдасів та методів за замовчуванням. Та все ж мені все одно нудно за перевіреними винятками. Наприклад, якщо я просто хочу перерахувати всі видимі поля об’єкта, я хотів би просто написати це:
Arrays.asList(p.getClass().getFields()).forEach(
f -> System.out.println(f.get(p))
);
Тим не менш, оскільки get
метод може кинути перевірений виняток, який не узгоджується з Consumer
контрактом на інтерфейс, я повинен вловити це виняток і написати наступний код:
Arrays.asList(p.getClass().getFields()).forEach(
f -> {
try {
System.out.println(f.get(p));
} catch (IllegalArgumentException | IllegalAccessException ex) {
throw new RuntimeException(ex);
}
}
);
Однак у більшості випадків я просто хочу, щоб виняток було викинуто як RuntimeException
і дозволяло програмі обробляти, чи ні, виняток без помилок компіляції.
Отже, я хотів би мати вашу думку щодо мого суперечливого вирішення роздратування перевірених винятків. З цією метою я створив допоміжний інтерфейс ConsumerCheckException<T>
та функцію утиліти rethrow
( оновлену відповідно до опису коментаря Doval ) наступним чином:
@FunctionalInterface
public interface ConsumerCheckException<T>{
void accept(T elem) throws Exception;
}
public class Wrappers {
public static <T> Consumer<T> rethrow(ConsumerCheckException<T> c) {
return elem -> {
try {
c.accept(elem);
} catch (Exception ex) {
/**
* within sneakyThrow() we cast to the parameterized type T.
* In this case that type is RuntimeException.
* At runtime, however, the generic types have been erased, so
* that there is no T type anymore to cast to, so the cast
* disappears.
*/
Wrappers.<RuntimeException>sneakyThrow(ex);
}
};
}
/**
* Reinier Zwitserloot who, as far as I know, had the first mention of this
* technique in 2009 on the java posse mailing list.
* http://www.mail-archive.com/javaposse@googlegroups.com/msg05984.html
*/
public static <T extends Throwable> T sneakyThrow(Throwable t) {
throw (T) t;
}
}
А тепер я можу просто написати:
Arrays.asList(p.getClass().getFields()).forEach(
rethrow(f -> System.out.println(f.get(p)))
);
Я не впевнений, що це найкраща ідіома, щоб розгорнути перевірені винятки, але, як я пояснив, я хотів би мати більш зручний спосіб досягнення мого першого прикладу, не маючи справу з перевіреними винятками, і це простіший спосіб, який я знайшов зробити це.
sneakyThrow
всередині, rethrow
щоб кинути оригінальний перевірений виняток, а не загортати його в RuntimeException
. Можна також використати @SneakyThrows
примітку від Project Lombok, яка робить те саме.
Consumer
s у forEach
може бути виконано паралельно при використанні паралельних Stream
s. Тоді перекидається, що піднімається від відмовлення від споживача, потім поширюється на викликову нитку, яка 1) не зупинить інших одночасно працюючих споживачів, що може бути, а може і не підходить, і 2) якщо більше одного з споживачів щось кине, тільки один із кидаючих предметів буде видно за викликом.