Guava 的可选使用(有例外)的快捷方式?

发布于 2024-12-21 16:30:31 字数 387 浏览 0 评论 0原文

我一直在编写特定的异常抛出程序,以防可选值不存在。

例如:

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

我发现这段代码不太流畅,尤其是 bang (!) 的使用。我宁愿写这样的东西:

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

Guava 中是否有我还没有找到的快捷方式?

I keep writing specific exception throwers in the case the Optional is absent.

For instance:

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

I find this code not very fluent, especially the use of the bang (!). I'd rather like writing something like:

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

Is there such a shortcut in Guava that I haven't found yet?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(7

挽清梦 2024-12-28 16:30:31

作为一名 Guava 开发人员,让我尝试解开这里的逻辑。回答原始问题以及直接针对该问题的评论线程:

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

也就是说,我同意您在这个特定问题中提到的行为有非常好的用例:“如果不存在,抛出一个例外。”也许您正在实现一个可以通过两种方式访问​​的类 - 一种方法具有可选返回值,另一种方法假设该值始终存在,否则抛出异常。例如,Deque 接口提供了 peek、poll 和 Offer 的特殊值和异常抛出版本。

综上所述,据我所知,真正的 Guava 方法是......

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

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

有时 Guava 不会提供明确的支持,因为对于这些用例,我们认为最好以“正常方式”完成。我想这里就是这种情况。

Speaking as a Guava developer, let me try to unpack the logic here. Responding both to the original question, and the comment thread directly on the question:

It is absolutely the case that we try to force Guava users to respect our standards of good programming habits. (Our standards are strongly influenced by e.g. Effective Java.)

That said, I agree that there are perfectly good use cases for the behavior you're referring to in this particular question: "if absent, throw an exception." Perhaps you're implementing a class that can be accessed both ways -- one method with an Optional return value, and one method that assumes that the value will always be present, throwing an exception otherwise. The Deque interface, for instance, provides special-valued and exception-throwing versions of peek, poll, and offer.

All that said, to the best of my understanding, the True Guava Way to do this is...

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

The "orThrow" method you propose requires reflection (!!), doesn't let you customize the exception with a useful message, etc. The "normal way" is perfectly readable and more efficient.

Sometimes Guava doesn't provide explicit support for things because for those use cases, we think it's best done just the "normal way." I think this is the case here.

天邊彩虹 2024-12-28 16:30:31

毫无价值的是,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

It is worth nothing that Java 8's Optional has an orElseThrow(Supplier) method that allows the requested behavior.

It is used like this:

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

Furthermore, a method in Java 10 was added, orElseThrow(), which throws a NoSuchElementException if no value is present.

Optional<Object> reference = Optional.empty();
reference.orElseThrow(); // Throws a new NoSuchElementException
萌︼了一个春 2024-12-28 16:30:31

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

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

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

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

Here's another way to do it without additions to Guava:

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

MyException has to be unchecked, but this does allow you to pass arguments to its constructor. And of course if you're doing it a lot you can store the Supplier somewhere and use it each place you need it.

Object result = optional.or(SomethingIsMissing.INSTANCE);
债姬 2024-12-28 16:30:31

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

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

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

编辑:

假设您有一个自定义类接收您需要检查的参数/字段名称,您可以有一个更好的方法,类似于先决条件做:

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);
      }
   }
}

I don't think this would belong to the library. I find it very rare to find a library that receives an instance of an exception to be throw in case something doesn't go as expected, especially because in many cases an exception must have a message indicating what went wrong.

That being said, you can create your own Optional class that does what you need. Or you can create your own OptionalHelper class where you have a method that does what you want:

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

EDIT:

Supposing you have a custom class that receives a parameter/ field name that you need to check, you could have a better approach similar to what Preconditions does:

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);
      }
   }
}
那伤。 2024-12-28 16:30:31

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

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()));

这可能可以进一步扩展,但对于我的用例来说,它已经足够并且易于理解。

This works for me (no reflection, just type inference):

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;
    }
}

Usage:

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

This probably can be extended even further, but for my use cases it's sufficient and easy to understand.

墨小墨 2024-12-28 16:30:31

请参阅此处的官方问题

决定:否 - 太昂贵,不是常见模式,可以只使用 !isPresent(),抛出

由于 optional.orThrow(new Exception()) 对性能不利,我更喜欢 static import,这与@timk的回答类似。

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());

See official issue here

Decision: NO - too expensive, not a common pattern, can just use !isPresent(), throw

Since optional.orThrow(new Exception()) is not good for performance, I prefer static import, which is similar with@timk 's answer.

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

Equal to

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

Static method

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

    };
}

===========

The stack trace looks like

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());
孤凫 2024-12-28 16:30:31

虽然有点晚了,但在 Guava 中这样做是一种优雅的方法:

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

使用以下辅助方法:

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

A little late to the party, but here's an elegant of doing this in Guava:

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

Using the following helper method:

public static Supplier supplyError(Supplier<Error> errorSupplier) {
    return () -> { throw errorSupplier.get(); };
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文