Java 以功能方式处理异常的更好方法

Java 以功能方式处理异常的更好方法,java,java-8,java-stream,Java,Java 8,Java Stream,当在Java8中使用FP习惯用法时,异常(尤其是检查过的异常)会严重中断程序逻辑流。以下是一个任意示例: String s1 = "oeu", s2 = "2"; Stream.of(s1, s2).forEach(s -> System.out.println(Optional.of(s).map(Integer::parseInt).get())); 当不可解析字符串出现异常时,上述代码将中断。但是假设我只想用一个默认值替换它,就像我可以用Optional一样: Strea

当在Java8中使用FP习惯用法时,异常(尤其是检查过的异常)会严重中断程序逻辑流。以下是一个任意示例:

String s1 = "oeu", s2 = "2";
Stream.of(s1, s2).forEach(s -> 
    System.out.println(Optional.of(s).map(Integer::parseInt).get()));
当不可解析字符串出现异常时,上述代码将中断。但是假设我只想用一个默认值替换它,就像我可以用
Optional
一样:

Stream.of(s1, s2).forEach(s -> 
   System.out.println(Optional.of(s)
                              .map(Integer::parseInt)
                              .orElse(-1)));
当然,这仍然失败,因为
Optional
只处理
null
s。我想要的东西如下:

Stream.of(s1, s2).forEach(s ->
    System.out.println(
        Exceptional.of(s)
                   .map(Integer::parseInt)
                   .handle(NumberFormatException.class, swallow())
                   .orElse(-1)));


注意:这是一个自我回答的问题。

下面给出的是
例外类的完整代码。它有一个相当大的API,它是
Optional
API的纯扩展,因此它可以作为任何现有代码中的替代品,但它不是final
Optional
类的子类型。该类可以被视为与monad具有相同的关系,就像
Optional
Maybe
monad一样:它从中汲取了灵感,但适应了Java习惯用法(例如实际抛出异常,甚至来自非终端操作)

以下是本课程遵循的一些关键指导原则:

  • 与一元方法相反,它不忽略Java的异常机制

  • 相反,它减轻了异常和高阶函数之间的阻抗失配

  • 异常处理不是静态类型安全的(由于鬼鬼祟祟的抛出),但在运行时总是安全的(除非显式请求,否则从不接受异常)

该类试图涵盖处理异常的所有典型方法:

  • recover
    使用一些提供替代值的处理代码
  • flatRecover
    类似于
    flatMap
    ,它允许返回一个新的
    异常
    实例,该实例将被展开并适当更新当前实例的状态
  • propagate
    异常,从
    exception
    表达式中抛出它,并使
    propagate
    调用声明此异常类型
  • 在包装成另一个异常后传播
    它(翻译它)
  • 句柄
    它,导致一个空的
    异常
  • 作为处理的一种特殊情况,
    使用空处理程序块吞下它
propagate
方法允许用户有选择地选择要从代码中公开哪些已检查异常。在调用终端操作时未处理的异常(如
get
)将在没有声明的情况下偷偷抛出。这通常被认为是一种高级且危险的方法,但通常被用作某种方式,以减轻检查异常与不声明异常的lambda形状相结合所带来的麻烦。
exceptive
类希望提供一个更干净、更具选择性的选择,以取代偷偷摸摸的投掷



如果允许
java.util.function
提供的每个函数接口抛出异常会怎么样

public interface ThrowingSupplier<R, X extends Throwable> {
    public R get() throws X;
}
(是一个永远不能抛出的
运行时异常


你最初的例子会变成

ThrowingFunction<String, Integer, NumberFormatException> parse = Integer::parseInt;
Function<String, Optional<Integer>> safeParse = parse.fallbackTo(s -> null)
    .andThen(Optional::ofNullable);
Stream.of(s1, s2)
    .map(safeParse)
    .map(i -> i.orElse(-1))
    .forEach(System.out::println);
ThrowingFunction parse=Integer::parseInt;
函数safeParse=parse.fallbackTo(s->null)
.第四(可选::不可用);
水流(s1、s2)
.map(安全解析)
.map(i->i.orElse(-1))
.forEach(System.out::println);

有一个名为的第三方库。它具有提供必要功能的monad。它还具有
TryMapFunction
TrySupplier
功能接口,可以使用
Try
monad,但有选中的异常。

以下是我以前在这个主题上做过的一些介绍

我做了一个关于推理的界面。
结果
要么是类型为
T
的成功,要么是类型为异常的失败。它是的一个子类型,作为一个立即完成的异步操作,但这在这里并不重要

创造结果-

Result.success( value )
Result.failure( exception )
Result.call( callable )
然后可以通过各种方式变换结果-
变换、映射、然后、偷看、捕捉、最后

Async<Integer> rInt = Result.success( s )
      .map( Integer::parseInt )
      .peek( System.out::println )
      .catch_( NumberFormatException.class, ex->42 ) // default
      .catch_( Exception.class, ex-> { ex.printStacktrace(); throw ex; } )
      .finally_( ()->{...} )
Async rInt=Result.success
.map(整数::parseInt)
.peek(System.out::println)
.catch(NumberFormatException.class,ex->42)//默认值
.catch_(Exception.class,ex->{ex.printStacktrace();throw ex;})
.最后(()->{…})

不幸的是,API关注的是异步,所以有些方法返回异步。其中一些可以被Result覆盖以返回Result;但有些不能,例如
then()
(这是平面地图)。但是如果有兴趣的话,,提取与异步无关的独立结果API很容易。

如果您决定将该类放入某个存储库,请不要忘记更新您的答案:)@8472 Stackoverflow=>另一个由Mario Fusco为java编写的“Try”monad示例可以在这里找到:。@MarkoTopolnik当编译器试图推断
X的类型时扩展可抛出的< /代码>它将:<代码>它指导解析优化α的实例化,以便如果可能的话,它不是一个检查异常类型< /代码>。有建议将一个类似的类型添加到C++中,在名称>代码>预期< /代码>下。在
中,预期的
应为
int
,但如果不是,原因是
异常
。请看——这与异常类似,但与异常的联系较少。实际上,我设计类是为了响应我在
Try
中看到的内容。它们只是重复了同一个著名的单子主题,在哈斯克尔之外,我并不觉得它特别实用。他们严格遵守纯FP、无国籍和不变性的宗教。几乎所有的东西都表示为类型转换,它比Java更适合Haskell这样的强大类型系统。而且,它们与你的头脑不太协调
@FunctionalInterface
public interface ThrowingSupplier<R, X extends Throwable> {
    public R get() throws X;

    default public Supplier<R> fallbackTo(Supplier<? extends R> supplier) {
        ThrowingSupplier<R, Nothing> t = supplier::get;
        return orTry(t)::get;
    }

    default public <Y extends Throwable> ThrowingSupplier<R, Y> orTry(
            ThrowingSupplier<? extends R, ? extends Y> supplier) {
        Objects.requireNonNull(supplier, "supplier");
        return () -> {
            try {
                return get();
            } catch (Throwable x) {
                try {
                    return supplier.get();
                } catch (Throwable y) {
                    y.addSuppressed(x);
                    throw y;
                }
            }
        };
    }
}
ThrowingFunction<String, Integer, NumberFormatException> parse = Integer::parseInt;
Function<String, Optional<Integer>> safeParse = parse.fallbackTo(s -> null)
    .andThen(Optional::ofNullable);
Stream.of(s1, s2)
    .map(safeParse)
    .map(i -> i.orElse(-1))
    .forEach(System.out::println);
Result.success( value )
Result.failure( exception )
Result.call( callable )
Async<Integer> rInt = Result.success( s )
      .map( Integer::parseInt )
      .peek( System.out::println )
      .catch_( NumberFormatException.class, ex->42 ) // default
      .catch_( Exception.class, ex-> { ex.printStacktrace(); throw ex; } )
      .finally_( ()->{...} )