拥有多个可关闭资源的类的正确用法

Nik*_*vić 5 java

对于用户代码,有几个选项可以正确关闭多个资源:

1.尝试资源

try (
  A a = new A();
  B b = new B();
  C c = new C()
) {
  // ...
}
Run Code Online (Sandbox Code Playgroud)

除了好而简短之外,这也是正确的。

2.番石榴 Closer

对于JDK7之前的版本,有Guava Closer,其用法如下:

Closer closer = Closer.create();
try {
  A a = closer.register(new A());
  B b = closer.register(new B());
  C c = closer.register(new C());
  // ...
} catch (Throwable e) { // must catch Throwable
  throw closer.rethrow(e);
} finally {
  closer.close();
}
Run Code Online (Sandbox Code Playgroud)

虽然时间稍长,但效果也很好(有关更多信息,请参见https://github.com/google/guava/wiki/ClosingResourcesExplained#closer


拥有多个资源的对象呢?

说我有:

public class P implements AutoCloseable {
  private A a;
  private B b;
  private C c;

  public P() {
    a = new A();
    b = new B();
    c = new C();
  }

  public close() {
    c.close();
    b.close();
    a.close();
  }
}
Run Code Online (Sandbox Code Playgroud)

此代码存在多个问题:

  • 如果构造函数抛出异常,则不会关闭任何内容(调用方没有要调用的实例close
  • 如果从抛出异常close,则不会关闭某些资源

1个2个都没有遭受这些问题的困扰。然而:

  • try-with-resources显然无法使用,因为P的生存期由调用方控制
  • 番石榴Closer似乎也不能使用。尽管它更灵活,但它不支持重投掷,这在构造函数中是必需的

对于没有太多样板的N资源,这里的正确模式是什么?该解决方案还应具有抑制属性12

mis*_*der 1

您可以使用围绕方法执行模式向资源包装类的用户隐藏资源的打开和关闭。这样您将确保资源始终处于关闭状态。您应该为不同的用例添加单独的操作方法。仅当这是公共资源并被应用程序的许多部分使用时,这才有用。

这是一个示例

public class ResourceWrapper {

    private A a;
    private B b;
    private C c;

    private ResourceWrapper() {
        // add try catch if you have to, after cleanup then throw exception if ithappens
        a = new A();
        b = new B();
        c = new C();
    }

    /**
     * add required operation methods
     */
    public ResourceWrapper op1() {
        // do some operations
        return this;
    }
    public ResourceWrapper op2() {
        // if additional add or different
        return this;
    }
    // close everything here
    private void close() {
        // check null if you have to
        // add try catch if you have to
        c.close();
        b.close();
        a.close();
    }

public static void use(Consumer<ResourceWrapper> consumer) {
    ResourceWrapper resource = null;
    try {
        resource = new ResourceWrapper();
        consumer.accept(resource);
    }
    finally {
        if(resource!=null) {
            resource.close();
        }
    }
}
}

public class SampleResourceUser {
    /*
     * This represents the user of the Resource,
     * User only cares about which operations that needs to be done on the resource.
     * Opening and closing the resource wrapped around the operation methods by the owner of the Resource.
     *
     */
    public static void main(String[] args) {
        ResourceWrapper.use(resource->resource.op1().op2());
    }
}
Run Code Online (Sandbox Code Playgroud)