【问题标题】:Shorthand for "If/Throw, Else/Return" logic in Java 8?Java 8 中“If/Throw, Else/Return”逻辑的简写?
【发布时间】:2021-09-09 17:59:22
【问题描述】:

在 Java 8 中是否有更短的 if/throw else/return 语法? java.util.Optional 提供了一种在一个语句中完成此操作的方法,但它需要为每个具有非空引用的调用创建一个 Optional 实例。

这可以在一个语句中完成吗?

public static MyEnum fromString(String value) {
    MyEnum result = enumMap.get(value);
    if (result == null)
        throw new IllegalArgumentException("Unsupported value: " + value);
    return result;
}

可选示例(不好,每次都需要可选实例)

public static MyEnum fromString(String value) {
    return Optional.ofNullable(enumMap.get(value)).orElseThrow(
        () -> new IllegalArgumentException("Unsupported value: " + value));
}

【问题讨论】:

  • 我会坚持你的第一个例子。它简单、清晰、简洁,只比需要我理解这个新的Optional 东西的示例多一行代码。
  • @JonathonReinhart 是的,实际上我同意你的看法。我在我的个人项目中经常遇到这种情况,这让我感到好奇。我是出于好奇而不是寻求技术优势。
  • 您是否使用 Optional 对代码进行了基准测试? VM 确实逃脱了分析,因此它有时可以在堆栈上分配对象,这应该非常快。
  • 在您的Optionals 中加入Map 不是更好吗?
  • @Dirk Lachowski:对于Map 中不存在的键,Map<…,Optional<…>> 仍会返回 null

标签: java java-8


【解决方案1】:

临时Optional 实例的影响可以忽略不计。通常 JVM 会检测到它的临时性质并优化掉实例。即使临时实例的创建没有被优化掉,一个临时对象对内存管理的影响也低得离谱。另见GC overhead of Optional<T> in Java

但是,如果地图是可变的,您可以使用以下技巧:

public static MyEnum fromString(String value) {
    return enumMap.computeIfAbsent(value, v -> {
        throw new IllegalArgumentException("Unsupported value: " + v); });
}

请注意,Map 未被此代码修改,但仍必须是可变的,因为不可变映射可能会在尝试使用 computeIfAbsent 时抛出 UnsupportedOperation 异常,而无需检查操作是否真的会修改映射.


但最后,Optional 并没有错。但请注意,您问题中的代码是错误的。传递给 Optional.orElseThrow 方法的 lambda 表达式旨在提供所需的异常,而不是抛出它:

public static MyEnum fromString(String value) {
    return Optional.ofNullable(enumMap.get(value)).orElseThrow(() ->
        new IllegalArgumentException("Unsupported value: " + value) // just return it
    );
}

【讨论】:

  • 所以语言中没有任何内置的东西,比如运算符,来处理这种情况。但是,通过对象的接口提供 orElseThrowcomputeIfAbsent 之类的方法来封装 if/else/throw 逻辑,或者至少提供一种更简单的实现方式,这似乎是一种很好的做法。
  • 我同意,在更高级别的接口中提供将回退操作作为参数的方法可能是一个好习惯。在最好的情况下,您可以将供应商/函数传递给较低级别​​的方法,例如后端映射的computeIfAbsent 或可选的orElseThrow。当方法可能失败时,将Optional 返回给调用者也是一个不错的选择。
  • 我认为使用 computeIfAbsent() 会限制您仅抛出未经检查的异常,这是否正确?
  • @Jeremy:没错。如果你想抛出检查异常,你必须使用Optional 的解决方案(正如最近讨论的here),无论如何这是更好的解决方案,因为它也适用于不可变的Maps 并且更容易理解.
【解决方案2】:

如果你愿意和NullPointerException一起生活,你可以这样做:

public static MyEnum fromString(String value) {
    return requireNonNull(enumMap.get(value), () -> "Unsupported: " + value);
}

假设import static java.util.Objects.requireNonNull

编辑:

如果你真的很在意你抛出的异常类型,只需实现你自己的静态实用方法:

static<T, X extends Throwable> T nonNullOrThrow(T val, Supplier<? extends X> exSupplier) throws X {
    if (val != null) return val;
    else throw exSupplier.get();
}

那你就可以了

return nonNullOrThrow(enumMap.get(value), () -> new IllegalArgumentException("unsupported: " + k));

【讨论】:

  • 太近了!我正在寻找一种允许提供不同例外的解决方案。如果只 requireNonNull 的供应商取而代之的是 Throwable!
  • 请注意,Supplier 的 lambda 表达式捕获了异常消息中使用的值,因此每次都会创建一个新的 Supplier 实例。这可能会打扰人们认为Optional 的单个实例是一个问题。如果您想避免创建每个实例,则必须使用 Function&lt;T,X&gt; 将 value 参数转换为异常,以便它可以保持不变并由 JRE 作为单例实现。
猜你喜欢
  • 2015-10-28
  • 1970-01-01
  • 2013-09-20
  • 2011-10-02
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-03-24
  • 1970-01-01
相关资源
最近更新 更多