首页 > 解决方案 > 对于未实现 AutoCloseable 的类,使用 lambda 是一种安全、正确且等效的解决方法吗?

问题描述

背景:我使用 Java 类InitialDirContext来访问 LDAP 目录。 不幸的是,它没有实现 interface AutoCloseable,因此不能在try-with-resources块中使用。

这是我写的原始代码:(受这个答案的启发

final Properties props = new Properties();
// Populate 'props' here.
final InitialDirContext context = new InitialDirContext(props);
Exception e0 = null;
try {
    // use 'context' here
}
catch (Exception e) {
    // Only save a reference to the exception.
    e0 = e;
    // Why re-throw?
    // If finally block does not throw, this exception must be thrown.
    throw e;
}
finally {
    try {
        context.close();
    }
    catch (Exception e2) {
        if (null != e0) {
            e0.addSuppressed(e2);
            // No need to re-throw 'e0' here.  It was (re-)thrown above.
        }
        else {
            throw e2;
        }
    }
}

这是一个安全、正确和等效的替代品吗?

try (final AutoCloseable dummy = () -> context.close()) {
    // use 'context' here
}

认为答案是肯定的,但我想确认一下。我尝试用谷歌搜索这种模式,但我什么也没找到。就是这么简单!因此,我怀疑它可能不正确。

编辑:我刚刚发现这个答案有类似的模式。

标签: javatry-catch-finallytry-with-resourcesautocloseable

解决方案


正如您链接到的另一个答案中所解释的那样,它不是严格等效的,因为您必须 catch 或 throw ExceptionfromAutoCloseable.close()并且您必须确保不要context在块之后做任何事情,try因为它不像InitialDirContext直接实现的那样超出范围AutoCloseable。我仍然同意其他人的观点,这种解决方法非常好。

当然,您也可以直接扩展InitialDirContext并使其实现AutoCloseable,或者(对于最终类)使用委托模式并包装目标对象。

package de.scrum_master.stackoverflow;

import javax.naming.NamingException;
import javax.naming.directory.InitialDirContext;
import java.util.Hashtable;
import java.util.Properties;

public class TryWithResourcesAutoCloseableWrapper {

  public static void main(String[] args) throws NamingException {
    final Properties props = new Properties();
    props.put("java.naming.factory.initial", "com.sun.jndi.dns.DnsContextFactory");
    variant1(props);
    variant2(props);
    variant3(props);
  }

  public static void variant1(Properties props) throws NamingException {
    final InitialDirContext context = new InitialDirContext(props);
    try (final AutoCloseable dummy = context::close) {
      lookupMX(context);
    }
    catch (NamingException ne) {
      throw ne;
    }
    catch (Exception e) {
      e.printStackTrace();
    }
  }

  public static void variant2(Properties props) throws NamingException {
    final InitialDirContext context = new InitialDirContext(props);
    try (final MyCloseable dummy = context::close) {
      lookupMX(context);
    }
  }

  public static void variant3(Properties props) throws NamingException {
    try (final MyInitialDirContext context = new MyInitialDirContext(props)) {
      lookupMX(context);
    }
  }

  private static void lookupMX(InitialDirContext context) throws NamingException {
    System.out.println(context.getAttributes("scrum-master.de", new String[] { "MX" }));
  }

  public interface MyCloseable extends AutoCloseable {
    void close() throws NamingException;
  }

  public static class MyInitialDirContext extends InitialDirContext implements AutoCloseable {
    public MyInitialDirContext(Hashtable<?, ?> environment) throws NamingException {
      super(environment);
    }
  }

}

关于如何使用这些解决方法的更多想法:

  • 两者variant1都以你永远不会使用的块内的对象variant2为代价,除非你先将它们投射到。相反,您当然可以直接使用外部对象,这也是您的建议。dummytryInitialDirContextcontext
  • variant3自动关闭context对象中直接具有正确的(子)类型,因此您实际上可以无缝地使用它而无需强制转换或虚拟对象。这是以特殊子类为代价的。

推荐阅读