Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Simulating python's With statement in java

Is there something like Python with context manager in Java?

For example say I want to do something like the following:

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

   c.close();
   return c;
}

where in python I just have:

with( C.getConnection().open() as c):
   Item i = c.query(itemID);
   return i;
like image 620
Andriy Drozdyuk Avatar asked May 31 '10 18:05

Andriy Drozdyuk


4 Answers

Java 7 has introduced a new feature to address this issue: "try with resources"

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

Close resource quietly using try-with-resources

The syntax is to place the resource in parentheses after the try keyword:

try (BufferedReader br = new BufferedReader(new FileReader(path))) {
    return br.readLine();
}

Prior to Java 7, you can use a finally block.

BufferedReader br = new BufferedReader(new FileReader(path));
try {
    return br.readLine();
} finally {
    if (br != null) br.close();
}
like image 101
RAAAAAAAANDY Avatar answered Nov 18 '22 23:11

RAAAAAAAANDY


There's an alternative using a generic wrapper like this:

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

NOTE: The Java way is the one you've just described. This other is just for "fun" and experimentation:

The _ is a generic wrapper and the with function is an utility class defined somewhere else as:

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

In strict theory, you could re-use it to perform other stuff, like deleting an item:

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

or update it

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

Without having to integrate the try/catch again.

Here's a full working demo that proofs the concept ( and doesn't do anything else )

like image 30
OscarRyz Avatar answered Nov 19 '22 00:11

OscarRyz


Not at the moment; Java still hasn't added syntactic sugar for this pattern. Still, it won't get as clean as with(Python) or using(C#), but you can at least clean that up a little bit by just having one call to c.close() inside a finally block, instead of twice as you've done:

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

This also brings it in line with how both with and using are actually implemented, which is a try..finally block (not a try..catch).

like image 7
tzaman Avatar answered Nov 18 '22 23:11

tzaman


As tzaman said, the secret is using finally; generally:

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

Things to note here:

  • try and finally each create a variable scope. So allocating your resource within the try clause won't work, as it won't be visible in the finally clause- you've got to declare the resource's variable before the try statement.

If you have several resources to allocate, the general pattern applies cleanly, but this is often not evident to beginners:

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

, and so on and so forth if you have more resources to allocate. If you have a couple of them, you will surely be tempted to try and avoid deep nesting of try... finally statements. Don't. You can get resource deallocation and exception handling right without nesting so many try... finally statements, but getting it right without nesting try... finally is even uglier than deep nesting.

If you frequently need to use a set of resources, you can implement a functor-based method to avoid the repetition, something like:

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

Which then you can use like this:

doWithResources(new WithResources() {
    public void doStuff(Resource1 r1, Resource2 r2) {
        // code goes here
    }
});

doWithResources will automatically handle allocation and deallocation correctly, and your code will have less repetition (which is a good thing). However:

  • Java's syntax for anonymous classes is excessively verbose
  • Checked exceptions within doStuff complicate things too much

, two points which I hope will be solved in Java 7.

You can find this kind of code throughout Spring, for instance:

  • http://static.springsource.org/spring/docs/3.0.x/spring-framework-reference/html/classic-spring.html#orm-hibernate-template ; doInHibernate thingy.
like image 3
alex Avatar answered Nov 18 '22 22:11

alex