对于未实现 AutoCloseable 的 类,使用 lambda 是否是一种安全、正确且等效的解决方法?
Is using a lambda a safe, correct, and equivalent workaround for classes that do not implement AutoCloseable?
背景:我使用JavaclassInitialDirContext
访问LDAP目录。 Unfortunately, it does not implement interface AutoCloseable
, so it cannot be used in try-with-resources 块。
下面是我写的原代码:(inspired by this answer)
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
}
我认为答案是肯定的,但我想确认一下。我尝试使用谷歌搜索这种模式,但一无所获。就是这么简单!因此,我怀疑它可能不正确。
编辑: 我刚刚发现 具有相似的模式。
正如您链接到的 中所解释的那样,它并不严格等效,因为您必须从 AutoCloseable.close()
中捕获或抛出 Exception
,并且您必须确保不这样做在 try
块之后带有 context
的任何内容,因为它没有超出范围,就好像 InitialDirContext
直接实现了 AutoCloseable
。我仍然同意其他人的看法,这个解决方法非常好。
当然,您也可以扩展 InitialDirContext
并使其直接实现 AutoCloseable
或(对于最终 类)使用委托模式并包装目标对象。
package de.scrum_master.Whosebug;
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
都以 dummy
对象为代价,这些对象在 try
块内你永远不会使用,除非你将它们转换为 InitialDirContext
首先。相反,您可以直接使用外部 context
对象,当然,这也是您的建议。
- 在
variant3
中,可自动关闭的 context
对象直接具有正确的(子)类型,因此您实际上可以无缝地使用它而无需强制转换或虚拟对象。这是以特殊子类为代价的。
背景:我使用JavaclassInitialDirContext
访问LDAP目录。 Unfortunately, it does not implement interface AutoCloseable
, so it cannot be used in try-with-resources 块。
下面是我写的原代码:(inspired by this answer)
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
}
我认为答案是肯定的,但我想确认一下。我尝试使用谷歌搜索这种模式,但一无所获。就是这么简单!因此,我怀疑它可能不正确。
编辑: 我刚刚发现
正如您链接到的 AutoCloseable.close()
中捕获或抛出 Exception
,并且您必须确保不这样做在 try
块之后带有 context
的任何内容,因为它没有超出范围,就好像 InitialDirContext
直接实现了 AutoCloseable
。我仍然同意其他人的看法,这个解决方法非常好。
当然,您也可以扩展 InitialDirContext
并使其直接实现 AutoCloseable
或(对于最终 类)使用委托模式并包装目标对象。
package de.scrum_master.Whosebug;
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
都以dummy
对象为代价,这些对象在try
块内你永远不会使用,除非你将它们转换为InitialDirContext
首先。相反,您可以直接使用外部context
对象,当然,这也是您的建议。- 在
variant3
中,可自动关闭的context
对象直接具有正确的(子)类型,因此您实际上可以无缝地使用它而无需强制转换或虚拟对象。这是以特殊子类为代价的。