在java中模拟python的With语句

时间:2010-05-31 18:36:56

标签: java python connection

在Java中是否存在类似Python 上下文管理器的内容?

例如说我想做类似以下的事情:

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 个答案:

答案 0 :(得分:18)

Java 7引入了一项新功能来解决这个问题:“尝试使用资源”

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

Close resource quietly using try-with-resources

语法是将资源放在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();
}

答案 1 :(得分:6)

目前不在; Java仍然没有为这种模式添加语法糖。尽管如此,它不会像with(Python)或using(C#)一样干净,但你至少可以通过调用c.close()来清理它。在finally区块内,而不是你做过的两次:

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

这也使它与withusing实际实现的方式一致,即try..finally块(不是try..catch)。

答案 2 :(得分:3)

正如特扎曼所说,秘密最终是在使用;一般:

Resource r = allocateResource();
try {
    // use resource
}
finally {
    r.dispose();
}

此处需要注意的事项:

  • 尝试并最终创建一个变量范围。因此,在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语句,但是在没有嵌套的情况下正确尝试...最终甚至比深度嵌套更难。

如果您经常需要使用一组资源,则可以实现基于仿函数的方法以避免重复,例如:

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

答案 3 :(得分:2)

在Java 7中引入了try-with-resources。在此之前,您必须使用try-finally块。请参阅此处的文档:https://docs.oracle.com/javase/tutorial/essential/exceptions/tryResourceClose.html

答案 4 :(得分:1)

有一种替代方法可以使用这样的通用包装:

 final _<Item> item = new _<Item>();
 final _<Connection> c = new _<Connection>();
 with( factory, c, new Runnable() {
    public void run(){
        item._ = c._.query( itemId );
    }
});
return item._;

注意:Java方式是您刚才描述的方式。另一个只是为了“有趣”和实验:

_是一个通用的包装器,with函数是在其他地方定义的实用程序类:

class WithUtil {
    public static void with( ConnectionFactory factory, 
                            _<Connection> c, Runnable block ) {
        try {
            c._ = factory.getConnection();
            c._.open();
            block.run();
        } catch( Exception ioe ){
        }finally{
            if( c._ != null ) try {
                c._.close();
            } catch( IOException ioe ){}
        }
    }
}

严格理论中,您可以重复使用它来执行其他操作,例如删除项目:

    public void deleteItem( final int itemId ) {
        final _<Connection> c = new _<Connection>();
        with( factory, c, new Runnable() {
            public void run(){
               Item item  = c._.query( itemId );
               if( ! item.hasChildren() ) {
                    c._.delete( item );
               }
            }
        });
    }

或更新

    public void update( final int itemId, String newName ) {
        final _<Connection> c = new _<Connection>();
        with( factory, c, new Runnable() {
            public void run(){
               Item item  = c._.query( itemId );
               item.setName( newName );
               c._.update( item );
            }
        });
    }

无需再次集成try / catch。

这是一个证明概念的full working demo(并且没有做任何其他事情)