为了账号安全,请及时绑定邮箱和手机立即绑定

对于未实现 AutoCloseable 的类,使用 lambda 是否是安全、正确且等效的解决方法?

对于未实现 AutoCloseable 的类,使用 lambda 是否是安全、正确且等效的解决方法?

慕神8447489 2023-10-19 21:23:25
背景:我使用 Java 类InitialDirContext来访问 LDAP 目录。 不幸的是,它没有实现接口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}我想答案是肯定的,但我想确认一下。我尝试用谷歌搜索这种模式,但一无所获。就是这么简单!因此,我怀疑它可能不正确。编辑:我刚刚发现这个答案具有类似的模式。
查看完整描述

1 回答

?
慕勒3428872

TA贡献1848条经验 获得超6个赞

因为您必须捕获或抛出,Exception并且必须确保不在块之后AutoCloseable.close()执行任何操作,因为它没有超出范围,就像直接实现一样。我仍然同意其他人的观点,即这个解决方法非常好。contexttryInitialDirContextAutoCloseable

当然,您也可以扩展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对象中直接具有正确的(子)类型,因此您实际上可以无缝地使用它,而无需转换或虚拟对象。这是以特殊子类为代价的。


查看完整回答
反对 回复 2023-10-19
  • 1 回答
  • 0 关注
  • 89 浏览

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信