代码之家  ›  专栏  ›  技术社区  ›  Andriy Drozdyuk Pickels

用java语言模拟python的With语句

  •  7
  • Andriy Drozdyuk Pickels  · 技术社区  · 14 年前

    具有

    getItem(itemID){
       Connection c = C.getConnection();
       c.open();
       try{
        Item i = c.query(itemID);
       }catch(ALLBunchOfErrors){
          c.close();
       }
    
       c.close();
       return c;
    }
    

    with( C.getConnection().open() as c):
       Item i = c.query(itemID);
       return i;
    
    5 回复  |  直到 14 年前
        1
  •  7
  •   tzaman    14 年前

    现在不行;Java仍然没有为这个模式添加语法糖。尽管如此,它还是不会像以前那样干净 with using c.close() 内部 finally 阻止,而不是两次:

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

    这也使它符合 实际上是实现的,这是一个 try..finally 块(不是 try..catch

        2
  •  26
  •   Burrito    6 年前

    Java7引入了一个新特性来解决这个问题:“try with resources”

    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();
    }
    

    BufferedReader br = new BufferedReader(new FileReader(path));
    try {
        return br.readLine();
    } finally {
        if (br != null) br.close();
    }
    
        3
  •  3
  •   alex    14 年前

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

    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();
    }
    

    ,依此类推,如果您有更多的资源要分配。如果你有两个,你肯定会试图避免尝试的深巢。。。最后是陈述。不要。您可以获得资源释放和异常处理的权利,而无需嵌套这么多尝试。。。最后一句话,但要想正确无需嵌套尝试。。。最后比深窝还要难看。

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

    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
        }
    });
    

    • Java的匿名类语法过于冗长
    • doStuff中的检查异常使事情变得太复杂

    您可以在整个Spring中找到此类代码,例如:

        4
  •  2
  •   OscarRyz    14 年前

    还有一种方法是使用这样的通用包装器:

     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 );
                }
            });
        }
    

    full working demo 这证明了这个概念(而且不做任何其他事情)

        5
  •  2
  •   fwc    9 年前

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