在Java中模拟Python的with语句

14 投票
5 回答
10300 浏览
提问于 2025-04-15 23:23

在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 个回答

3

正如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中找到这种代码,比如:

34

Java 7 引入了一个新功能来解决这个问题:叫做“带资源的 try”。

http://docs.oracle.com/javase/tutorial/essential/exceptions/tryResourceClose.html

使用带资源的 try 安静地关闭资源

它的写法是在 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();
}
7

目前还没有;Java 还没有为这种写法添加更简洁的语法。不过,它的写法不会像 Python 的 with 或 C# 的 using 那样简洁,但你至少可以通过在 finally 块中只调用一次 c.close() 来稍微简化一下,而不是像你之前那样调用两次:

try {
    // use c
} finally {
    c.close()
}

这样做也和 withusing 的实际实现方式一致,都是用 try..finally 块,而不是 try..catch

撰写回答