java - A better approach to handling exceptions in a functional way -
exceptions, checked ones, can severely interrupt flow of program logic when fp idiom used in java 8. here arbitrary example:
string s1 = "oeu", s2 = "2"; stream.of(s1, s2).foreach(s -> system.out.println(optional.of(s).map(integer::parseint).get())); the above code breaks when there's exception unparseable string. want replace default value, can optional:
stream.of(s1, s2).foreach(s -> system.out.println(optional.of(s) .map(integer::parseint) .orelse(-1))); of course, still fails because optional handles nulls. follows:
stream.of(s1, s2).foreach(s -> system.out.println( exceptional.of(s) .map(integer::parseint) .handle(numberformatexception.class, swallow()) .orelse(-1))); note: self-answered question.
presented below full code of exceptional class. has quite large api pure extension of optional api can drop-in replacement in existing code—except isn't subtype of final optional class. class can seen being in same relationship try monad optional maybe monad: draws inspiration it, adapted java idiom (such throwing exceptions, non-terminal operations).
these key guidelines followed class:
as opposed monadic approach, doesn't ignore java's exception mechanism;
instead relieves impedance mismatch between exceptions , higher-order functions;
exception handling not statically typesafe (due sneaky throwing), safe @ runtime (never swallows exception except on explicit request).
the class tries cover typical ways handle exception:
recoverhandling code provides substitute value;flatrecoverwhich, analogousflatmap, allows return newexceptionalinstance unwrapped , state of current instance suitably updated;propagateexception, throwingexceptionalexpression , makingpropagatecall declare exception type;propagateafter wrapping exception (translate it);handleit, resulting in emptyexceptional;- as special case of handling,
swallowempty handler block.
the propagate approach allows 1 selectively pick checked exceptions wants expose code. exceptions remain unhandled @ time terminal operation called (like get) sneakily thrown without declaration. considered advanced , dangerous approach, nevertheless employed way alleviate nuisance of checked exceptions in combination lambda shapes not declare them. exceptional class hopes offer cleaner , more selective alternative sneaky throw.
/* * copyright (c) 2015, marko topolnik. rights reserved. * * licensed under apache license, version 2.0 (the "license"); * may not use file except in compliance license. * may obtain copy of license @ * * http://www.apache.org/licenses/license-2.0 * * unless required applicable law or agreed in writing, software * distributed under license distributed on "as is" basis, * without warranties or conditions of kind, either express or implied. * see license specific language governing permissions , * limitations under license. */ import java.util.nosuchelementexception; import java.util.objects; import java.util.function.consumer; import java.util.function.function; import java.util.function.predicate; import java.util.function.supplier; public final class exceptional<t> { private final t value; private final throwable exception; private exceptional(t value, throwable exc) { this.value = value; this.exception = exc; } public static <t> exceptional<t> empty() { return new exceptional<>(null, null); } public static <t> exceptional<t> ofnullable(t value) { return value != null ? of(value) : empty(); } public static <t> exceptional<t> of(t value) { return new exceptional<>(objects.requirenonnull(value), null); } public static <t> exceptional<t> ofnullableexception(throwable exception) { return exception != null? new exceptional<>(null, exception) : empty(); } public static <t> exceptional<t> ofexception(throwable exception) { return new exceptional<>(null, objects.requirenonnull(exception)); } public static <t> exceptional<t> from(trysupplier<t> supplier) { try { return ofnullable(supplier.tryget()); } catch (throwable t) { return new exceptional<>(null, t); } } public static exceptional<void> fromvoid(tryrunnable task) { try { task.run(); return new exceptional<>(null, null); } catch (throwable t) { return new exceptional<>(null, t); } } public static <e extends throwable> consumer<? super e> swallow() { return e -> {}; } public t get() { if (value != null) return value; if (exception != null) sneakythrow(exception); throw new nosuchelementexception("no value present"); } public t orelse(t other) { if (value != null) return value; if (exception != null) sneakythrow(exception); return other; } public t orelseget(supplier<? extends t> other) { if (value != null) return value; if (exception != null) sneakythrow(exception); return other.get(); } public<u> exceptional<u> map(function<? super t, ? extends u> mapper) { objects.requirenonnull(mapper); if (value == null) return new exceptional<>(null, exception); final u u; try { u = mapper.apply(value); } catch (throwable exc) { return new exceptional<>(null, exc); } return ofnullable(u); } public<u> exceptional<u> flatmap(function<? super t, exceptional<u>> mapper) { objects.requirenonnull(mapper); return value != null ? objects.requirenonnull(mapper.apply(value)) : empty(); } public exceptional<t> filter(predicate<? super t> predicate) { objects.requirenonnull(predicate); if (value == null) return this; final boolean b; try { b = predicate.test(value); } catch (throwable t) { return ofexception(t); } return b ? : empty(); } public <x extends throwable> exceptional<t> recover( class<? extends x> exctype, function<? super x, t> mapper) { objects.requirenonnull(mapper); return exctype.isinstance(exception) ? ofnullable(mapper.apply(exctype.cast(exception))) : this; } public <x extends throwable> exceptional<t> recover( iterable<class<? extends x>> exctypes, function<? super x, t> mapper) { objects.requirenonnull(mapper); (class<? extends x> exctype : exctypes) if (exctype.isinstance(exception)) return ofnullable(mapper.apply(exctype.cast(exception))); return this; } public <x extends throwable> exceptional<t> flatrecover( class<? extends x> exctype, function<? super x, exceptional<t>> mapper) { objects.requirenonnull(mapper); return exctype.isinstance(exception) ? objects.requirenonnull(mapper.apply(exctype.cast(exception))) : this; } public <x extends throwable> exceptional<t> flatrecover( iterable<class<? extends x>> exctypes, function<? super x, exceptional<t>> mapper) { objects.requirenonnull(mapper); (class<? extends x> c : exctypes) if (c.isinstance(exception)) return objects.requirenonnull(mapper.apply(c.cast(exception))); return this; } public <e extends throwable> exceptional<t> propagate(class<e> exctype) throws e { if (exctype.isinstance(exception)) throw exctype.cast(exception); return this; } public <e extends throwable> exceptional<t> propagate(iterable<class<? extends e>> exctypes) throws e { (class<? extends e> exctype : exctypes) if (exctype.isinstance(exception)) throw exctype.cast(exception); return this; } public <e extends throwable, f extends throwable> exceptional<t> propagate( class<e> exctype, function<? super e, ? extends f> translator) throws f { if (exctype.isinstance(exception)) throw translator.apply(exctype.cast(exception)); return this; } public <e extends throwable, f extends throwable> exceptional<t> propagate( iterable<class<e>> exctypes, function<? super e, ? extends f> translator) throws f { (class<? extends e> exctype : exctypes) if (exctype.isinstance(exception)) throw translator.apply(exctype.cast(exception)); return this; } public <e extends throwable> exceptional<t> handle(class<e> exctype, consumer<? super e> action) { if (exctype.isinstance(exception)) { action.accept(exctype.cast(exception)); return empty(); } return this; } public <e extends throwable> exceptional<t> handle(iterable<class<e>> exctypes, consumer<? super e> action) { (class<? extends e> exctype : exctypes) if (exctype.isinstance(exception)) { action.accept(exctype.cast(exception)); return empty(); } return this; } public <x extends throwable> t orelsethrow(supplier<? extends x> exceptionsupplier) throws x { if (value != null) return value; if (exception != null) sneakythrow(exception); throw exceptionsupplier.get(); } public boolean ispresent() { return value != null; } public void ifpresent(consumer<? super t> consumer) { if (value != null) consumer.accept(value); if (exception != null) sneakythrow(exception); } public boolean isexception() { return exception != null; } @override public boolean equals(object obj) { if (this == obj) return true; return obj instanceof exceptional && objects.equals(value, ((exceptional)obj).value); } @override public int hashcode() { return objects.hashcode(value); } @suppresswarnings("unchecked") private static <t extends throwable> void sneakythrow(throwable t) throws t { throw (t) t; } } @functionalinterface public interface trysupplier<t> { t tryget() throws throwable; } @functionalinterface public interface tryrunnable { void run() throws throwable; }
Comments
Post a Comment