Guava可选用于例外的快捷方式?

时间:2011-12-16 12:53:44

标签: java guava

在不存在Optional的情况下,我一直在写特定的例外投掷者。

例如:

Optional<?> optional = ...;
if (!optional.isPresent()) {
  throw new MyException();
}
Object result = optional.get();

我发现这段代码不是很流利,尤其是使用bang(!)。我更喜欢写一些类似的东西:

Optional<?> optional = ...;
Object result = optional.orThrow(MyException.class);

我还没有找到番石榴这样的捷径吗?

7 个答案:

答案 0 :(得分:21)

作为Guava开发人员,让我尝试在这里解压缩逻辑。直接回答问题的原始问题和评论主题:

绝对是这样,我们试图强迫Guava用户尊重我们良好的编程习惯标准。 (我们的标准强烈受到例如Effective Java的影响。)

那就是说,我同意在这个特定问题中你所指的行为有很好的用例:“如果缺席,就抛出异常。”也许你正在实现一个可以双向访问的类 - 一个带有Optional返回值的方法,一个假定值总是存在的方法,否则抛出异常。例如,Deque接口提供peek,poll和offer的特殊值和异常抛出版本。

所有这一切,据我所知,真正的番石榴方式是......

if (value.isPresent()) {
  return value.get();
} else {
  throw new MyException();
}

您提出的“orThrow”方法需要反射(!!),不允许您使用有用的消息等自定义异常。“正常方式”是完全可读且更有效的。

有时候Guava没有提供明确的支持,因为对于那些用例,我们认为最好只采用“正常方式”。我想这就是这种情况。

答案 1 :(得分:16)

Java 8的Optional具有允许所请求行为的orElseThrow(Supplier)方法,这是值得的。

它的用法如下:

Optional<Object> reference = Optional.empty()
reference.orElseThrow(MyOwnException::new); // Throws a new MyOwnException

此外,还添加了Java 10中的方法orElseThrow(),如果没有值,则抛出NoSuchElementException

Optional<Object> reference = Optional.empty();
reference.orElseThrow(); // Throws a new NoSuchElementException

答案 2 :(得分:13)

这是另一种不添加Guava的方法:

Object result = optional.or(new Supplier() {
    public Object get() {
        throw new MyException();
    }
});

必须取消选中MyException,但这确实允许您将参数传递给其构造函数。当然,如果您正在做很多事情,您可以将供应商存储在某个地方,并在您需要的地方使用它。

Object result = optional.or(SomethingIsMissing.INSTANCE);

答案 3 :(得分:2)

我认为这不属于图书馆。我发现很难找到一个接收异常实例的库,以防万一没有按预期运行,特别是因为在很多情况下异常必须有一条消息指示出了什么问题。

话虽这么说,你可以创建自己的Optional类来满足你的需要。或者,您可以创建自己的OptionalHelper类,其中有一个方法可以执行您想要的操作:

public class OptionalHelper {
   public <T> T valueOrThrow(final Optional<T> optional) throws MyException {
      if (optional.isPresent()) {
          return optional.get();
      } else {
          throw new MyException();
      }
   }
}

编辑:

假设您有一个自定义类,它接收您需要检查的参数/字段名称,您可以采用类似于Preconditions的更好方法:

public class OptionalHelper {
   public <T> T valueOrFail(final Optional<T> optional, final String fieldName) throws OptionalNotPresentError {
      if (optional.isPresent()) {
          return optional.get();
      } else {
          throw new OptionalNotPresentError(fieldName);
      }
   }
}

答案 4 :(得分:1)

这对我有用(没有反思,只是类型推断):

public class ExceptionSupplier<T, E extends RuntimeException> implements Supplier<T> {

    private final E exception;

    private ExceptionSupplier(E exception) {
        this.exception = exception;
    }

    public static <T, E extends RuntimeException> ExceptionSupplier<T, E> throwA(E exception) {
        return new ExceptionSupplier<T, E>(exception);
    }    

    public static <T, E extends RuntimeException> ExceptionSupplier<T, E> throwA(@SuppressWarnings("UnusedParameters") Class<T> class_, E exception) {
        return new ExceptionSupplier<T, E>(exception);
    }

    @Override
    public T get() {
        throw exception;
    }
}

用法:

Something something = optionalSomething.or(throwA(Something.class, new SomeException()));

这可能会进一步扩展,但对于我的用例,它足够且易于理解。

答案 5 :(得分:0)

请参阅official issue here

  

决定:不 - 太贵,不是常见的模式,可以使用!isPresent(),throw

由于optional.orThrow(new Exception())对性能不利,我更喜欢静态导入,这与@timk 's answer类似。

Result result = optional.or(throwException());

等于

Result result = optional.or(SomeSupplier.throwException());

静态方法

public static Supplier<Result> throwException() {
    return new Supplier<Result>() {
        @Override
        public Result get() {
            throw new RuntimeException();
        }

    };
}

===========

堆栈跟踪看起来像

Exception ... RuntimeException
at SomeSupplier$1.get(SomeSupplier.java:line where throw RuntimeException)
at SomeSupplier$1.get(SomeSupplier.java:1)
at com.google.common.base.Absent.or(Absent.java:60)
at line where call optional.or(throwException());

答案 6 :(得分:0)

聚会晚了一点,但这是在番石榴里做这件事的一种优雅方式:

Optional<?> optional = ...;
Object result = optional.or(supplyError(MyException::new));

使用以下辅助方法:

public static Supplier supplyError(Supplier<Error> errorSupplier) {
    return () -> { throw errorSupplier.get(); };
}