【问题标题】:Abstracting try/catch with Function in Java 8在 Java 8 中使用函数抽象 try/catch
【发布时间】:2015-12-25 11:58:13
【问题描述】:

我最近发现自己编写了许多如下形式的块:

try {
    return Optional.of(thing.doSomething(arg));
} catch (Exception e) {
    System.out.println(e.getMessage());
    return Optional.empty();
}

这是必要的,因为某些方法发出信号表明它们可能会抛出异常,如果我不在这些方法周围加上 try/catch 块,Eclipse 就会对我大喊大叫。

所以我写了这个:

public static <A, T> Optional<T> tryOpt (Function<A, T> f, A arg) {
    try {
        return Optional.of(f.apply(arg));
    } catch (Exception e) {
        System.out.println(e.getMessage());
        return Optional.empty();
    }
}

因此,我传递给 tryOpt 的任何函数都包含在 try/catch 块中并安全执行,其结果作为 Optional 返回。但是 Eclipse 仍然因为我使用它而对我大喊大叫:

return tryOpt(
          ((x) -> thing.doSomething(x)),
          arg);

我的问题是,我有什么方法可以告诉 Eclipse 和/或 java 编译器没关系,并且我 am 间接地将有问题的方法包含在 try/catch 块中?或者我只是误解了java的工作原理,在这种情况下,有人会介意启发我吗?或者,另一方面,在这种情况下,我可以安全地忽略 Eclipse 的红线警告吗?

【问题讨论】:

  • 如果我不使用 try/catch 块包围这些方法,Eclipse 会冲我大喊:不。如果你没有捕捉到异常或抛出它,它就会对你大喊大叫。返回一个空的可选项而不是发出问题的信号很可能不是处理异常的适当方式。只需抛出异常。或者捕获它,将其包装到适当抽象级别的另一个异常中,然后抛出该新异常。如果您不想强制调用者处理它,则该新异常可以是 RuntimeException。
  • 捕捉Exception 尤其不是一个好主意:它捕捉一切,包括您实际上不打算处理的异常(如NullPointerException 等),隐藏错误,并返回不正确的结果而不是发出问题的信号。

标签: java eclipse functional-programming java-8


【解决方案1】:

Function.apply 不允许抛出异常,因此当您传入执行该操作的 lambda 表达式时编译器会报错。

您可以通过使用允许抛出异常的功能接口来解决此问题。 (也许这样的接口已经存在于其中一个 java.util.* 包中):

public static <A, T> Optional<T> tryOpt (Fn<A, T> f, A arg) {
    try {
        return Optional.of(f.apply(arg));
    } catch (Exception e) {
        System.out.println(e.getMessage());
        return Optional.empty();
    }
}


public static interface Fn<A, T> {
      T apply(A a) throws Exception;
}

【讨论】:

    【解决方案2】:

    如果是红线,则不是警告,是错误。

    在这种情况下,Function&lt;A,R&gt; 接口声明了一个抛出异常的方法,但您的实现却抛出了异常。在这种情况下,最好的办法是创建自己的函数式接口,声明一个会引发异常的方法,例如

    public interface SupplierWithException<R> {R get();}
    

    你可以这样使用它:

    return asOptional(() -> thing.doSomething(arg1, arg2, arg3));
    

    但是,我建议简单地吞下异常是不好的形式;如果您经常使用asOptional 方法,这表明您的代码部分(您使用的库?)之间存在阻抗不匹配。您可以将异常重新抛出为RuntimeExceptions,或者使用像 RxJava 这样显式处理异常的东西。

    【讨论】:

      【解决方案3】:

      主要问题是 lambdas 如何与已检查异常混在一起,这种设计方式可能会导致尴尬的解决方案,例如将每次调用包装到使用它们的每个 lambda 中抛出的方法。

      我最终使用了一个为我管理这个的包装器,例如:

        @FunctionalInterface
        public interface FunctionThrowing<T, R>
        {
          R apply(T t) throws Exception;
        }
      
      
        public static <T, R> Function<T, R> wrapFunction(FunctionThrowing<T, R> function)
        {
          return a -> {
            try
            {
              return function.apply(a);
            } catch (Exception exception)
            {
              throw exception;
              return null;
            }
          };
        }
      

      这基本上是通过返回一个处理已检查异常并将其转换为未检查异常的函数来包装一个函数,这样您就不需要用 try/catch 块包装每个调用,但您仍然不会失去能力捕获异常(因为它变得未选中)。

      【讨论】:

      • “lambdas 如何与检查异常混合”——不,这里没有交互。你反对的是 lambdas 对检查的异常没有做任何特殊的事情。这只是实现功能接口的一种方式。如果函数式接口方​​法允许您抛出给定的异常,则 lambda 可以抛出该异常,否则不会。与通过子类化实现功能接口没有什么不同。
      【解决方案4】:

      现代的做法:

      interface VoidCallable {
          void call() throws Exception;
      }
      
      public static void tryCatchBlockWrapper(VoidCallable func) {
          try {
              func.call();
          } catch (IOException e) {
              // do something
          } catch (Exception e) {
              e.printStackTrace();
          }
      }
      
      public void functionToWrap() {
          tryCatchBlockWrapper(() -> throw new IOException());   
      }
      

      附:如果你想添加上下文(传递更多参数),你可以添加它们:

      public static void tryCatchBlockWrapper(String anotherArg, VoidCallable func) {
          // ...
      }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2020-01-21
        • 2010-09-21
        • 2021-11-25
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多