繁体   English   中英

我的AutoCloseable.close()实现可以检测到潜在的异常吗?

[英]Can my AutoCloseable.close() implementation detect a potential exception?

在实现AutoCloseable以使用Java 7 try-with-resources语句时,我想知道try块中是否存在异常。 例如:

class C implements AutoCloseable {
    @Override
    public void close() {
        if (exceptionOccurred)
            something();
        else
            somethingElse();
    }
}

为了说明这一点:

try (C c = new C()) {

    // This should cause a call to "something()"
    if (something)
        throw new RuntimeException();

    // This should cause a call to "somethingElse()"
    else
        ;
}

现在,从了解try-with-resources语句如何转换为字节码 ,我想这是不可能的。 但是有没有(可靠!)技巧通过instrumentation / reflection /一些未记录的编译器功能,允许我从AutoCloseable.close()访问上面的RuntimeException

注意:我是一名API设计师,我无法控制API使用者的资源尝试代码。 因此,必须在AutoCloseable站点上完成实现

执行此操作的常规方法是在try块的末尾显式调用。 例如:

try (CustomTransaction transaction = ...) {
    // Do something which might throw an exception...

    transaction.commitOnClose();
}

然后在close ,你要么根据是否调用commitOnClose()而中止事务或提交它。

它不是自动的,但它实现起来非常简单 - 而且阅读起来非常简单。

我一直在努力解决这个问题。 我不喜欢Jon Skeet的回答,因为开发人员(即我)可能会意外忘记调用commitOnClose() 我想要一种方法让开发人员在离开代码块时被强制调用commit()或rollback()。

Lambda和已检查的异常并不能很好地协同工作,所以正确的解决方案有点令人费解,但最终我和我的同事想出了一段允许你这样工作的代码:

TransactionEnforcer.DbResult<String> result = transactionEnforcer.execute(db -> {
  try {
    db.someFunctionThatThrowsACheckedException();
  } catch (TheException e) {
    return failure("fallback value");
  }
  return success(db.getAFancyValue());
});

result.ifPresent(v -> System.out.println(v));

注意如何返回值,可以检查代码是否成功,并且java的代码路径返回检查强制您始终明确是否应该提交代码。

它使用以下代码实现:

package nl.knaw.huygens.timbuctoo.database;

import java.util.Optional;
import java.util.function.Function;
import java.util.function.Supplier;

public class TransactionEnforcer {
  private final Supplier<DbClass> dbClassFactory;

  public TransactionEnforcer(Supplier<DbClass> dbClassFactory) {
    this.dbClassFactory = dbClassFactory;
  }

  public <U> DbResult<U> execute(Function<DbClass, DbResult<U>> actions) {
    DbClass db = dbClassFactory.get();
    try {
      DbResult<U> result = actions.apply(db);
      if (result.isSuccess()) {
        db.close(true);
      } else {
        db.close(false);
      }
      return result;
    } catch (RuntimeException e) {
      db.close(false);
      throw e;
    }
  }

  public static class DbResult<T> {
    private final Optional<T> value;
    private final boolean success;

    private DbResult(T value, boolean success) {
      this.value = Optional.of(value);
      this.success = success;
    }

    public static <T> DbResult<T> success(T value) {
      return new DbResult<T>(value, true);
    }

    public static <T> DbResult<T> success() {
      return new DbResult<T>(null, true);
    }

    public static <T> DbResult<T> failure(T value) {
      return new DbResult<T>(value, false);
    }

    public static <T> DbResult<T> failure() {
      return new DbResult<T>(null, false);
    }

    public boolean isSuccess() {
      return success;
    }

    public Optional<T> getValue() {
      return value;
    }
  }
}

(我将DbClass作为练习留给读者)

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM