当前位置: 首页 > 面试题库 >

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

徐德海
2023-03-14
问题内容

当在Java 8中使用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仅处理nulls。我想要以下内容:

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

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


问题答案:

下面显示的是Exceptional该类的完整代码。它具有相当大的API,是对OptionalAPI 的纯扩展,因此可以在任何现有代码中直接替换它-
除非它不是最终Optional类的子类型。这个类可以被看作是与相同的关系是Try因为单子Optional是与Maybe单子:它的设计灵感来自它,但是适应了Java的成语(如实际抛出异常,甚至从非终端操作)。

这些是班级遵循的一些关键准则:

  • 与monadic方法相反,它不会忽略Java的异常机制;

  • 相反,它可以消除异常和高阶函数之间的阻抗不匹配;

  • 异常处理不是静态的类型安全(由于偷偷摸摸的抛出),但是在运行时始终是安全的(除明确请求外,绝不吞咽异常)。

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

  • recover 带有一些提供替代值的处理代码;
  • flatRecover类似于flatMap,它允许返回一个新Exceptional实例,该实例将被解包并适当更新当前实例的状态;
  • propagate异常,将其从Exceptional表达式中抛出,并使propagate调用声明此异常类型;
  • propagate包装到另一个异常后( 翻译 );
  • handle它,导致一个空的Exceptional
  • 作为处理的一种特殊情况,swallow它带有一个空的处理程序块。

propagate方法允许一个人有选择地从他的代码中选择他想公开哪些检查过的异常。在调用终端操作时仍未处理的异常(如get)将被不加声明地
偷偷
抛出。这通常被认为是一种先进且危险的方法,但是仍然经常被用作某种方式,以与未声明它们的lambda形状相结合,在某种程度上减轻受检查的异常的滋扰。该Exceptional班希望能提供一个更清洁,更具有选择性替代偷偷摸摸扔。

/*
 * Copyright (c) 2015, Marko Topolnik. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the 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 Stream<T> stream() { 
      return value == null ? Stream.empty() : Stream.of(value); 
  }

  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 ? this : 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);
    for (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);
    for (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 {
    for (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
  {
    for (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) {
    for (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;
}


 类似资料:
  • 本文向大家介绍Spring MVC中异常处理的三种方式,包括了Spring MVC中异常处理的三种方式的使用技巧和注意事项,需要的朋友参考一下 前言 在 SpringMVC, SpringBoot 处理 web 请求时, 若遇到错误或者异常,返回给用户一个良好的错误信息比 Whitelabel Error Page 好的多。 SpringMVC 提供了三种异常处理方式, 良好的运用它们可以给用户提

  • 本文向大家介绍SpringMVC统一异常处理三种方法详解,包括了SpringMVC统一异常处理三种方法详解的使用技巧和注意事项,需要的朋友参考一下 这篇文章主要介绍了SpringMVC-统一异常处理三种方法详解,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下 在 Spring MVC 应用的开发中,不管是对底层数据库操作,还是业务层或控制层操作,都

  • 本文向大家介绍浅谈RxJava处理业务异常的几种方式,包括了浅谈RxJava处理业务异常的几种方式的使用技巧和注意事项,需要的朋友参考一下 本文介绍了RxJava处理业务异常的几种方式,分享给大家。具体如下: 关于异常 Java的异常可以分为两种:运行时异常和检查性异常。 运行时异常: RuntimeException类及其子类都被称为运行时异常,这种异常的特点是Java编译器不去检查它,也就是说

  • 本文向大家介绍深入剖析Java中的各种异常处理方式,包括了深入剖析Java中的各种异常处理方式的使用技巧和注意事项,需要的朋友参考一下 1. 调试追踪代码: 2. 抛出Exception,没有finally,当catch遇上return     后台输出结果: 3. 抛出Exception,当catch体里有return,finally体的代码块将在catch执行return之前被执行     后

  • 问题内容: 目前,我的代码段的工作方式类似于发生错误时,它通过一条消息并在几秒钟后消失,我这样做了,即使成功响应,成功消息也会在几秒钟后出现并消失。但由于某些原因,我现在不需要这样。 在这里,您可以查看我当前的摘录: 在以上代码段中,如果我不使用,则成功和错误将并存。 例如:一个用户提交错误数据并得到错误消息,而在提交正确数据并获得成功消息后,此时屏幕上同时存在成功和错误消息,这很奇怪 我想要类似

  • 本文向大家介绍简单学习5种处理Vue.js异常的方法,包括了简单学习5种处理Vue.js异常的方法的使用技巧和注意事项,需要的朋友参考一下 错误大全 为了测试各种异常处理技巧,我故意触发三种类型的错误。 第一种:引用一个不能存在的变量: 上述代码运行后不会抛出错误,但是在控制台会有[Vue warn]消息。 你可以在Codepen查看例子完整代码。 第二种:将变量绑定到一个被计算出来的属性,计算的