Okay, the title is maybe hard to understand. I didn't find something correct.
So, basically I'm using Java 8 functions to create a Retryable API. I wanted an easy implementation of these interfaces, so I created an of(...) method in each implementation of the Retryable interface where we can use lambda expressions, instead of creating manually an anonymous class.
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Supplier;
public interface Retryable<T, R> extends Function<T, R>{
void retrying(Exception e);
void skipping(Exception e);
int trials();
#Override
default R apply(T t) {
int trial = 0;
while (true) {
trial++;
try {
return action(t);
} catch (Exception e) {
if (trial < trials()) {
retrying(e);
} else {
skipping(e);
return null;
}
}
}
}
R action(T input) throws Exception;
interface RunnableRetryable extends Retryable<Void, Void> {
static RunnableRetryable of(Consumer<Exception> retrying, Consumer<Exception> skipping, int trials, CheckedRunnable runnable) {
return new RunnableRetryable() {
#Override
public void retrying(Exception e) {
retrying.accept(e);
}
#Override
public void skipping(Exception e) {
skipping.accept(e);
}
#Override
public int trials() {
return trials;
}
#Override
public Void action(Void v) throws Exception {
runnable.tryRun();
return null;
}
};
}
#FunctionalInterface
interface CheckedRunnable extends Runnable {
void tryRun() throws Exception;
#Override
default void run() {
try {
tryRun();
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
}
interface ConsumerRetryable<T> extends Retryable<T, Void> {
static <T> ConsumerRetryable of(Consumer<Exception> retrying, Consumer<Exception> skipping, int trials, CheckedConsumer<T> consumer) {
return new ConsumerRetryable<T>() {
#Override
public void retrying(Exception e) {
retrying.accept(e);
}
#Override
public void skipping(Exception e) {
skipping.accept(e);
}
#Override
public int trials() {
return trials;
}
#Override
public Void action(T t) throws Exception {
consumer.tryAccept(t);
return null;
}
};
}
#FunctionalInterface
interface CheckedConsumer<T> extends Consumer<T> {
void tryAccept(T t) throws Exception;
#Override
default void accept(T t) {
try {
tryAccept(t);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
}
interface SupplierRetryable<T> extends Retryable<Void, T> {
static <T> SupplierRetryable of(Consumer<Exception> retrying, Consumer<Exception> skipping, int trials, CheckedSupplier<T> supplier) {
return new SupplierRetryable<T>() {
#Override
public void retrying(Exception e) {
retrying.accept(e);
}
#Override
public void skipping(Exception e) {
skipping.accept(e);
}
#Override
public int trials() {
return trials;
}
#Override
public T action(Void v) throws Exception {
return supplier.tryGet();
}
};
}
#FunctionalInterface
interface CheckedSupplier<T> extends Supplier<T> {
T tryGet() throws Exception;
#Override
default T get() {
try {
return tryGet();
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
}
interface FunctionRetryable<T, R> extends Retryable<T, R> {
static <T, R> FunctionRetryable of(Consumer<Exception> retrying, Consumer<Exception> skipping, int trials, CheckedFunction<T, R> function) {
return new FunctionRetryable<T, R>() {
#Override
public void retrying(Exception e) {
retrying.accept(e);
}
#Override
public void skipping(Exception e) {
skipping.accept(e);
}
#Override
public int trials() {
return trials;
}
#Override
public R action(T t) throws Exception {
return function.tryApply(t);
}
};
}
#FunctionalInterface
interface CheckedFunction<T, R> extends Function<T, R> {
R tryApply(T t) throws Exception;
#Override
default R apply(T t) {
try {
return tryApply(t);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
}
}
But as you can see, there's a lot of duplicate code in every of(...) methods. I could create a kind of "constructor" (that's not the correct word, because interfaces can't have a constructor) in the Retryable interface, but I don't know how. Does someone have an idea ?
The main problem is your API explosion. All these nested interfaces extending Retryable do not add any functionality, but require the user of this code to deal with them, once they are part of the API. Further, they are the cause of this code duplication, as each of these redundant interfaces requires its own implementation, whereas all implementations are basically doing the same.
After removing these obsolete types, you can simply implement the operations as delegation:
public interface Retryable<T, R> extends Function<T, R>{
void retrying(Exception e);
void skipping(Exception e);
int trials();
#Override default R apply(T t) {
try { return action(t); }
catch(Exception e) {
for(int trial = 1; trial < trials(); trial++) {
retrying(e);
try { return action(t); } catch (Exception next) { e=next; }
}
skipping(e);
return null;
}
}
R action(T input) throws Exception;
public static Retryable<Void, Void> of(Consumer<Exception> retrying,
Consumer<Exception> skipping, int trials, CheckedRunnable runnable) {
return of(retrying, skipping, trials, x -> { runnable.tryRun(); return null; });
}
#FunctionalInterface interface CheckedRunnable extends Runnable {
void tryRun() throws Exception;
#Override default void run() {
try { tryRun(); } catch (Exception e) { throw new RuntimeException(e); }
}
}
public static <T> Retryable<T, Void> of(Consumer<Exception> retrying,
Consumer<Exception> skipping, int trials, CheckedConsumer<T> consumer) {
return of(retrying, skipping, trials,
value -> { consumer.tryAccept(value); return null; });
}
#FunctionalInterface interface CheckedConsumer<T> extends Consumer<T> {
void tryAccept(T t) throws Exception;
#Override default void accept(T t) {
try { tryAccept(t); } catch (Exception e) { throw new RuntimeException(e); }
}
}
public static <T> Retryable<Void, T> of(Consumer<Exception> retrying,
Consumer<Exception> skipping, int trials, CheckedSupplier<T> supplier) {
return of(retrying, skipping, trials, voidArg -> { return supplier.tryGet(); });
}
#FunctionalInterface interface CheckedSupplier<T> extends Supplier<T> {
T tryGet() throws Exception;
#Override default T get() {
try { return tryGet(); }
catch (Exception e) { throw new RuntimeException(e); }
}
}
public static <T, R> Retryable<T, R> of(Consumer<Exception> retrying,
Consumer<Exception> skipping, int trials, CheckedFunction<T, R> function) {
return new Retryable<T, R>() {
#Override public void retrying(Exception e) { retrying.accept(e); }
#Override public void skipping(Exception e) { skipping.accept(e); }
#Override public int trials() { return trials; }
#Override public R action(T t) throws Exception {
return function.tryApply(t);
}
};
}
#FunctionalInterface interface CheckedFunction<T, R> extends Function<T, R> {
R tryApply(T t) throws Exception;
#Override default R apply(T t) {
try { return tryApply(t); }
catch (Exception e) { throw new RuntimeException(e); }
}
}
}
There is only one implementation class needed, which has to be able to deal with an argument and a return value, the others can simply delegate to it using an adapter function, doing either, dropping the argument or returning null, or both.
For most use cases, the shape of the lambda expression is appropriate to select the right method, e.g.
Retryable<Void,Void> r = Retryable.of(e -> {}, e -> {}, 3, () -> {});
Retryable<Void,String> s = Retryable.of(e -> {}, e -> {}, 3, () -> "foo");
Retryable<Integer,Integer> f = Retryable.of(e -> {}, e -> {}, 3, i -> i/0);
but sometimes, a little hint is required:
// braces required to disambiguate between Function and Consumer
Retryable<String,Void> c = Retryable.of(e->{}, e ->{}, 3,
str -> { System.out.println(str); });
It looks like you can factor some of this out in to a (possibly package-private) abstract class:
abstract class AbstractRetryable<T, R> implements Retryable<T, R> {
private final Consumer<Exception> retrying;
private final Consumer<Exception> skipping;
private final int trials;
AbstractRetryable(Consumer<Exception> retrying,
Consumer<Exception> skipping,
int trials) {
this.retrying = Objects.requireNonNull(retrying, "retrying");
this.skipping = Objects.requireNonNull(skipping, "skipping");
this.trials = trials;
}
#Override
public void retrying(Exception x) {
retrying.accept(x);
}
#Override
public void skipping(Exception x) {
skipping.accept(x);
}
#Override
public int trials() {
return trials;
}
}
The only issue with this is that you're using subinterfaces, so you can't create an anonymous class which both extends the abstract class and implements the subinterface.
You could then write more (again, possibly package-private) subclasses:
final class RunnableRetryableImpl
extends AbstractRetryable<Void, Void>
implements RunnableRetryable {
private final CheckedRunnable runnable;
RunnableRetryableImpl(Consumer<Exception> retrying,
Consumer<Exception> skipping,
int trials,
CheckedRunnable runnable) {
super(retrying, skipping, trials);
this.runnable = Objects.requireNonNull(runnable, "runnable");
}
#Override
public Void apply(Void ignored) {
try {
runnable.tryRun();
} catch (Exception x) {
// BTW I would consider doing this.
if (x instanceof RuntimeException)
throw (RuntimeException) x;
// I would also probably write a class like:
// class RethrownException extends RuntimeException {
// RethrownException(Exception cause) {
// super(cause);
// }
// }
// This way the caller can catch a specific type if
// they want to.
// (See e.g. java.io.UncheckedIOException)
throw new RuntimeException(x);
}
return null;
}
}
Or you could reduce the line count by using local classes:
static RunnableRetryable of(Consumer<Exception> retrying,
Consumer<Exception> skipping,
int trials,
CheckedRunnable runnable) {
Objects.requireNonNull(runnable, "runnable");
final class RunnableRetryableImpl
extends AbstractRetryable<Void, Void>
implements RunnableRetryable {
RunnableRetryable() {
// Avoid explicitly declaring parameters
// and passing arguments.
super(retrying, skipping, trials);
}
#Override
public Void apply(Void ignored) {
try {
runnable.tryRun();
} catch (Exception x) {
if (x instanceof RuntimeException)
throw (RuntimeException) x;
throw new RuntimeException(x);
}
return null;
}
}
return new RunnableRetryableImpl();
}
Personally, I think I would just write package-private implementations instead of the local classes but it certainly requires a fair amount of boiler-plate code.
Also, as a side note, when you are writing factories that return anonymous classes, you should use requireNonNull inside the method itself (as I did in my example of method). This is so that if null is passed to the method, the method throws the NPE instead of e.g. some call to retrying or skipping throwing the NPE some time later.
Related
While going through generics, I am not able to understand why the error is coming:
class Box <T> {
private T theThing;
public Box( T t) { theThing = t; }
public void reset( T t) { theThing = t; }
}
class WordBox< S extends CharSequence > extends Box< String > {
public WordBox( S t) { super(t.toString().toLowerCase()); }
public void reset( S t) {
// super.reset(t.toString().toLowerCase());
}
}
public class ss {
public static void main(String[] args) {
WordBox<String> city = new WordBox<String>("Skogland");
city.reset("Stavanger"); // error: ambiguous**
}
}
I can understand, method worldbox.reset () is not overriding the method from BOX, instead it is overloading.
After type erasure, I am assuming this will be code :
class Box {
private Object theThing;
public Box( Object t) { theThing = t; }
public void reset( Object t) { theThing = t; }
}
class WordBox extends Box{
public WordBox( CharSequence t) { super(t.toString().toLowerCase()); }
public void reset( CharSequence t) {
super.reset(t.toString().toLowerCase());
}
}
public class ss {
public static void main(String[] args) {
WordBox<String> city = new WordBox<String>("Skogland");
city.reset("Stavanger");
}
}
So, city.reset("Stavanger") should call the method from Worldbox.reset , as String extends charsequence and it seems to be closest match.
Could anyone please explain why the ambiguous error is coming in this code?
I would like to generalize the following pattern:
setChangeListener = c -> {
try {
// do something dangerous
} catch (final IOException e) {
logger.error(e.getLocalizedMessage(), e);
}
};
I would like to use it like this:
errorLoggingSetChangeListener = c -> {
// do something dangerous
};
I was thinking about this:
public class ErrorLoggingSetChangeListener<T> implements SetChangeListener<T> {
private static final Logger logger = Logger.getLogger(ErrorLoggingSetChangeListener.class);
private final SetChangeListener<T> delegate;
#Override
public void onChanged(final SetChangeListener.Change<? extends T> change) {
try {
delegate.onChanged(change);
} catch (final Exception e) {
if (logger.isEnabledFor(Level.ERROR)) {
logger.error(e.getLocalizedMessage(), e);
}
}
}
public ErrorLoggingSetChangeListener(final SetChangeListener<T> delegate) {
super();
this.delegate = delegate;
}
}
But that is not possible, since ErrorLoggingSetChangeListener is not a Functional interface.
Any chance to convert this class to an Functional Interface?
This does not compile:
public interface ErrorLoggingSetChangeListener<T> extends SetChangeListener<T> {
static final Logger logger = Logger.getLogger(ErrorLoggingSetChangeListener.class);
#Override
default void onChanged(final SetChangeListener.Change<? extends T> change) {
try {
SetChangeListener.super.onChanged(change);
} catch (final Exception e) {
if (logger.isEnabledFor(Level.ERROR)) {
logger.error(e.getLocalizedMessage(), e);
}
}
}
}
This does also not compile:
errorLoggingSetChangeListener = new ErrorLoggingSetChangeListener<>(c -> {
throw new IOException();
});
The error message is
Unhandled exception [..]
.
This is similar to #JonnyAW's solution, but combines both classes into a single interface:
import javafx.collections.SetChangeListener;
#FunctionalInterface
public interface ErrorLoggingSetChangeListener<E> extends SetChangeListener<E> {
public void delegate(Change<? extends E> change) throws Exception ;
#Override
public default void onChanged(Change<? extends E> change) {
try {
delegate(change);
} catch (Exception exc) {
// just do a System.out.println here to demo we reach this block:
System.out.println("Custom error handling...");
exc.printStackTrace();
}
}
}
And here's a demo of using this:
import javafx.collections.FXCollections;
import javafx.collections.ObservableSet;
public class Test {
public static void main(String[] args) {
ObservableSet<String> set = FXCollections.observableSet();
ErrorLoggingSetChangeListener<String> listener = c -> {
if (c.wasAdded()) {
int i = Integer.parseInt(c.getElementAdded());
System.out.println("Value added: "+i);
}
};
set.addListener(listener);
set.add("42");
set.add("What do you get when you multiply 6 by 9?");
}
}
which generates the expected output:
Value added: 42
Custom error handling...
java.lang.NumberFormatException: For input string: "What do you get when you multiply 6 by 9?"
at java.lang.NumberFormatException.forInputString(NumberFormatException.java:65)
at java.lang.Integer.parseInt(Integer.java:580)
at java.lang.Integer.parseInt(Integer.java:615)
at Test.lambda$0(Test.java:10)
at ErrorLoggingSetChangeListener.onChanged(ErrorLoggingSetChangeListener.java:12)
at com.sun.javafx.collections.SetListenerHelper$SingleChange.fireValueChangedEvent(SetListenerHelper.java:163)
at com.sun.javafx.collections.SetListenerHelper.fireValueChangedEvent(SetListenerHelper.java:72)
at com.sun.javafx.collections.ObservableSetWrapper.callObservers(ObservableSetWrapper.java:128)
at com.sun.javafx.collections.ObservableSetWrapper.add(ObservableSetWrapper.java:269)
at Test.main(Test.java:17)
here is my implementation, that will compile:
ErrorLoggingSetChangeListener:
import javafx.collections.SetChangeListener;
public class ErrorLoggingSetChangeListener<T> implements SetChangeListener<T> {
private DangerousInterface<T> delegate;
public ErrorLoggingSetChangeListener(DangerousInterface<T> delegate) {
super();
this.delegate = delegate;
}
#Override
public void onChanged(Change<? extends T> change) {
try {
this.delegate.delegate(change);
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
DangerousInterface:
public interface DangerousInterface<T> {
public void delegate(Change<? extends T> change) throws Exception;
}
Main:
SetChangeListener<String> listener = new ErrorLoggingSetChangeListener<>((test) -> {
//no errors here now
throw new Exception();
});
I got definitely no compile errors
EDIT: ok, I got the Problem, you need a new Interface that can actually throw something, now you can wrap it in onChanged
I'm having trouble rethrowing exceptions thrown by stream while "accessing" it.
For example, if I have a stream which throws ExceptionA:
Stream<String> stream = Stream.of("dummy").map(d -> {throw new ExceptionA();});
try {
stream.collect(Collectors.toList());
} catch (ExceptionA e) {}
What I want to achieve is create new stream2 out of stream without consuming stream which will throw ExceptionB when it's collected
try {
stream2.collect(Collectors.toList());
} catch (ExceptionB e) {}
Obviously
Iterator<String> newIt = createRethrowingIterator(stream.iterator());
Stream<String> stream2 = StreamSupport.stream(Spliterators.spliteratorUnknownSize(newIt, Spliterator.NONNULL), false)
where createRethrowingIterator wraps original iterator and returns new one which actually rethrows ExceptionA to ExceptionB
is not what I want as stream.iterator() is terminal operator, i.e. it will consume stream, which might lead to memory problems if stream is really large
This task is better solved using Spliterator rather than Iterator. It simplifies the logic, as you only have to implement a single method, tryAdvance, by delegating the the source’s tryAdvance method.
It also opens the opportunity for performance improvements by delegating the methods characteristics() and estimateSize() to the source, as the exception translation feature does not change them. You can also get decent parallel support, by implementing trySplit via delegating to the source. You only have to wrap the result exactly like the first Spliterator:
public class Rethrowing<T,E extends Throwable> implements Spliterator<T> {
public static <E extends Throwable, T> Stream<T> translateExceptions(
Stream<T> source, Class<E> catchType,
Function<? super E, ? extends RuntimeException> translator) {
return StreamSupport.stream(new Rethrowing<>(
source.spliterator(), catchType, translator), source.isParallel());
}
private final Spliterator<T> source;
private final Class<E> catchType;
private final Function<? super E, ? extends RuntimeException> translator;
public Rethrowing(Spliterator<T> sp, Class<E> catchType,
Function<? super E, ? extends RuntimeException> translator) {
this.source = sp;
this.catchType = catchType;
this.translator = translator;
}
#Override public boolean tryAdvance(Consumer<? super T> action) {
try { return source.tryAdvance(action); }
catch(Throwable t) {
if(catchType.isInstance(t))
throw translator.apply(catchType.cast(t));
else throw t;
}
}
#Override public int characteristics() {
return source.characteristics();
}
#Override public long estimateSize() {
return source.estimateSize();
}
#Override public Spliterator<T> trySplit() {
Spliterator<T> split = source.trySplit();
return split==null? null: new Rethrowing<>(split, catchType, translator);
}
}
you can use this utility class like
class ExceptionA extends IllegalStateException {
public ExceptionA(String s) {
super(s);
}
}
class ExceptionB extends IllegalStateException {
public ExceptionB(Throwable cause) {
super(cause);
}
}
Rethrowing.translateExceptions(
Stream.of("foo", "bar", "baz", "", "extra")
.peek(s -> System.err.println("consuming \""+s+'"'))
.map(s -> { if(s.isEmpty()) throw new ExceptionA("empty"); return s; }),
ExceptionA.class, ExceptionB::new)
.forEach(s -> System.err.println("terminal operation on "+s));
to get
consuming "foo"
terminal operation on foo
consuming "bar"
terminal operation on bar
consuming "baz"
terminal operation on baz
consuming ""
Exception in thread "main" ExceptionB: ExceptionA: empty
…
Caused by: ExceptionA: empty
…
Here, ExceptionB::new is the translation function, which is equivalent to exA->new ExceptionB(exA).
Why don't you wrap that call which throws your ExceptionA into a mapping function which if thrown transforms it into ExceptionB immediately, like:
try {
List<T> l = stream.map(o -> wrapped(() -> o.whateverThrowsExceptionA())).collect(toList());
// or do your stream2 operations first, before collecting the list
} catch (ExceptionB b) {
// handle your exception
}
where wrapped in that case would be similar to:
<T> T wrapped(Callable<T> o) throws ExceptionB {
try {
return callable.call();
} catch (Exception e) {
throw new ExceptionB(e);
}
}
You may even want to adjust the wrapper to take in a custom ExceptionA-catching function.
Alright, I failed to understand that terminal operation does not mean that stream is fully consumed. Thank you Louis Wasserman for clarifying this.
To demonstrate it I wrote some unit tests:
import org.junit.Test;
import java.util.Iterator;
import java.util.Spliterator;
import java.util.Spliterators;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import java.util.stream.StreamSupport;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
/**
* #author Beka Tsotsoria
*/
public class StreamExceptionRethrowingTest {
#Test
public void throwingIteratorMustBeConsumedWhenStreamIsCollected() throws Exception {
ThrowingIterator itToBeConsumed = new ThrowingIterator();
assertThatThrownBy(() -> streamFromIterator(itToBeConsumed)
.collect(Collectors.toList()))
.isInstanceOf(ExceptionA.class);
assertThat(itToBeConsumed.consumed()).isTrue();
}
#Test
public void throwingIteratorMustNotBeConsumedUntilNewStreamIsCollected() throws Exception {
ThrowingIterator itNotToBeConsumed = new ThrowingIterator();
RethrowingIterator rethrowingIterator = new RethrowingIterator(streamFromIterator(itNotToBeConsumed).iterator());
assertThat(itNotToBeConsumed.consumed()).isFalse();
Stream<String> stream2 = streamFromIterator(rethrowingIterator);
assertThat(itNotToBeConsumed.consumed()).isFalse();
assertThatThrownBy(() -> stream2
.collect(Collectors.toList()))
.hasCauseInstanceOf(ExceptionA.class)
.isInstanceOf(ExceptionB.class);
assertThat(itNotToBeConsumed.consumed()).isTrue();
}
#Test
public void streamIteratorMustNotBeConsumedUntilNewStreamIsCollected() throws Exception {
Stream<String> stream = Stream.of("dummy")
.map(d -> {
throw new ExceptionA();
});
Stream<String> stream2 = streamFromIterator(new RethrowingIterator(stream.iterator()));
// No exceptions so far, i.e. stream.iterator() was not consumed
assertThatThrownBy(() -> stream2
.collect(Collectors.toList()))
.hasCauseInstanceOf(ExceptionA.class)
.isInstanceOf(ExceptionB.class);
}
private Stream<String> streamFromIterator(Iterator<String> it) {
return StreamSupport.stream(Spliterators.spliteratorUnknownSize(it, Spliterator.NONNULL), false);
}
static class ThrowingIterator implements Iterator<String> {
private boolean hasNextCalled;
private boolean nextCalled;
#Override
public boolean hasNext() {
hasNextCalled = true;
throw new ExceptionA();
}
#Override
public String next() {
nextCalled = true;
throw new ExceptionA();
}
public boolean consumed() {
return hasNextCalled || nextCalled;
}
}
static class RethrowingIterator implements Iterator<String> {
private Iterator<String> it;
public RethrowingIterator(Iterator<String> it) {
this.it = it;
}
#Override
public boolean hasNext() {
try {
return it.hasNext();
} catch (ExceptionA e) {
throw new ExceptionB(e);
}
}
#Override
public String next() {
try {
return it.next();
} catch (ExceptionA e) {
throw new ExceptionB(e);
}
}
}
static class ExceptionA extends RuntimeException {
}
static class ExceptionB extends RuntimeException {
public ExceptionB(Throwable cause) {
super(cause);
}
}
}
Thanks for your comments. Cheers!
I have a legacy class C1, implementing interface I, that may throw some exceptions.
I want to create a class C2, also implementing interface I, that is based on an instance of C1, but catches all exceptions and does something useful about them.
Currently my implementation looks like this:
class C2 implements I {
C1 base;
#Override void func1() {
try {
base.func1();
} catch (Exception e) {
doSomething(e);
}
}
#Override void func2() {
try {
base.func2();
} catch (Exception e) {
doSomething(e);
}
}
...
}
(Note: I could also make C2 extend C1. This does not matter for the current question).
The interface contains many functions, so I have to write the same try... catch block again and again.
Is there a way to reduce the amount of code duplication here?
You can make a Proxy, it could actually be generic
interface I1 {
void test();
}
class C1 implements I1 {
public void test() {
System.out.println("test");
throw new RuntimeException();
}
}
class ExceptionHandler implements InvocationHandler {
Object obj;
ExceptionHandler(Object obj) {
this.obj = obj;
}
#Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
try {
return method.invoke(obj, args);
} catch (Exception e) {
// need a workaround for primitive return types
return null;
}
}
static <T> T proxyFor(Object obj, Class<T> i) {
return (T) Proxy.newProxyInstance(obj.getClass().getClassLoader(), new Class[] { i },
new ExceptionHandler(obj));
}
}
public class Test2 {
public static void main(String[] args) throws Exception {
I1 i1 = ExceptionHandler.proxyFor(new C1(), I1.class);
i1.test();
}
}
I am working on some workflow and it is possible to raise many exceptions in that. I heard that we can keep all those possible exceptions in an Enum (Exception1, Exception2 ...) and use it. How can we do that using Enums in Java?
You can add the classes of exceptions with
enum EnumWithExceptions {
ENUM1(Exception1.class, Exception2.class),
ENUM2(Exception3.class);
private final Class<? extends Exception>[] exceptions;
private EnumWithExceptions(Class<? extends Exception>... exceptions) {
this.exceptions = exceptions;
}
public boolean matches(Exception e) {
for(Class<? extends Exception> e2: exceptions)
if (e2.isInstance(e)) return true;
return false;
}
}
} catch(Exception e){
if (ENUM1.matches(e)){
//do something
} else if(ENUM2.matches(e)) {
//do something
} else {
//do something
}
}
enum Fred {
SAM(AnException.class),
I(AnotherException.class),
AM(YetAnotherException.class)
;
private Throwable t;
Fred(Throwable throwable) {
this.t = throwable;
}
public Throwable getThrowable() {
return t;
}
}
...
throw Fred.SAM.getThrowable();
Why not store the exceptions in an ArrayList? Or if you want to name the index, you could use a HashMap.
import java.util.ArrayList;
import java.util.HashMap;
public final class ExceptionStorage {
private static int exceptionCount = 0;
private static HashMap<String, Exception> indexedExceptions = new HashMap<>();
private static ArrayList<Exception> exceptions = new ArrayList();
public static void addException(Exception e) {
exceptions.add(e);
}
public static void putException(Exception e) {
indexedExceptions.put("Exception" + (++exceptionCount), e);
}
public static ArrayList<Exception> getUnindexedExceptions() {
return this.exceptions;
}
public static HashMap<String, Exception> getIndexedExceptions() {
return this.indexedExceptions;
}
}
Obviously you would have to modify the code to use either ArrayList or HashMap, but I think this would be a better solution than using Enums.