在Java中模拟Python的with语句
在Java中有没有类似Python的with上下文管理器的东西呢?
比如说,我想做一些像下面这样的事情:
getItem(itemID){
Connection c = C.getConnection();
c.open();
try{
Item i = c.query(itemID);
}catch(ALLBunchOfErrors){
c.close();
}
c.close();
return c;
}
而在Python中,我只需要这样写:
with( C.getConnection().open() as c):
Item i = c.query(itemID);
return i;
5 个回答
正如tzaman所说,关键在于使用finally;一般来说:
Resource r = allocateResource();
try {
// use resource
}
finally {
r.dispose();
}
这里有几点需要注意:
- try和finally各自会创建一个变量的作用域。所以在try里面分配的资源在finally里面是看不到的——你必须在try语句之前声明资源的变量。
如果你有多个资源需要分配,这个一般的模式是适用的,但对于初学者来说,这一点往往不太明显:
Resource1 r1 = allocateResource1();
try {
// code using r1, but which does not need r2
Resource r2 = allocateResource2();
try {
// code using r1 and r2
}
finally {
r2.dispose();
}
}
finally {
r1.dispose();
}
如果你有更多的资源要分配,情况也是如此。如果你有几个资源,你可能会想尽量避免深层嵌套try...finally语句。不要这样做。你可以在不嵌套那么多try...finally语句的情况下正确处理资源的释放和异常处理,但如果不嵌套try...finally,代码会变得更加复杂。
如果你经常需要使用一组资源,可以实现一种基于函数对象的方法来避免重复,比如:
interface WithResources {
public void doStuff(Resource1 r1, Resource2 r2);
}
public static void doWithResources(WithResources withResources) {
Resource r1 = allocateResource1();
try {
Resource r2 = allocateResource2();
try {
withResources.doStuff(r1, r2);
}
finally {
r2.dispose();
}
}
finally {
r1.dispose();
}
}
然后你可以这样使用:
doWithResources(new WithResources() {
public void doStuff(Resource1 r1, Resource2 r2) {
// code goes here
}
});
doWithResources会自动正确处理资源的分配和释放,这样你的代码就会减少重复(这是一件好事)。但是:
- Java的匿名类语法太冗长了
- 在doStuff中使用的检查异常让事情变得太复杂
这两点我希望在Java 7中能够得到解决。
你可以在Spring中找到这种代码,比如:
Java 7 引入了一个新功能来解决这个问题:叫做“带资源的 try”。
http://docs.oracle.com/javase/tutorial/essential/exceptions/tryResourceClose.html
它的写法是在 try 关键字后面的小括号里放入资源:
try (BufferedReader br = new BufferedReader(new FileReader(path))) {
return br.readLine();
}
在 Java 7 之前,你可以使用 finally 块来处理。
BufferedReader br = new BufferedReader(new FileReader(path));
try {
return br.readLine();
} finally {
if (br != null) br.close();
}
目前还没有;Java 还没有为这种写法添加更简洁的语法。不过,它的写法不会像 Python 的 with
或 C# 的 using
那样简洁,但你至少可以通过在 finally
块中只调用一次 c.close()
来稍微简化一下,而不是像你之前那样调用两次:
try {
// use c
} finally {
c.close()
}
这样做也和 with
和 using
的实际实现方式一致,都是用 try..finally
块,而不是 try..catch
。