Tutorial :Best groovy closure idiom replacing java inner classes?



Question:

As new to groovy...

I'm trying to replace the java idiom for event listeners, filters, etc.

My working code in groovy is the following:

def find() {      ODB odb = ODBFactory.open(files.nodupes); // data nucleus object database      Objects<Prospect> src = odb.getObjects(new QProspect());        src.each { println it };        odb.close();    }    class QProspect extends SimpleNativeQuery {      public boolean match(Prospect p) {          if (p.url) {              return p.url.endsWith(".biz");          }          return false;      }  }  

Now, this is far from what I'm used to in java, where the implementation of the Query interface is done right inside the odb.getObjects() method. If I where to code "java" I'd probably do something like the following, yet it's not working:

Objects<Prospect> src = odb.getObjects( {          boolean match(p) {               if (p.url) {              return p.url.endsWith(".biz");          }              return false;           }      } as SimpleNativeQuery);  

Or better, I'd like it to be like this:

 Objects<Prospect> src = odb.getObjects(         { it.url.endsWith(".biz") } as SimpleNativeQuery   );  

However, what groovy does it to associate the "match" method with the outer script context and fail me.

I find groovy... groovy anyways so I'll stick to learning more about it. Thanks.


What I should've asked was how do we do the "anonymous" class in groovy. Here's the java idiom:

void defReadAFile() {      File[] files = new File(".").listFiles(new FileFilter() {          public boolean accept(File file) {              return file.getPath().endsWith(".biz");          }      });  }  

Can groovy be as concise with no additional class declaration?


Solution:1

I think it would have helped you to get answers if you'd abstracted the problem so that it didn't rely on the Neodatis DB interface -- that threw me for a loop, as I've never used it. What I've written below about it is based on a very cursory analysis.

For that matter, I've never used Groovy either, though I like what I've seen of it. But seeing as no one else has answered yet, you're stuck with me :-)

I think the problem (or at least part of it) may be that you're expecting too much of the SimpleNativeQuery class from Neodatis. It doesn't look like it even tries to filter the objects before it adds them to the returned collection. I think instead you want to use org.neodatis.odb.impl.core.query.criteria.CriteriaQuery. (Note the "impl" in the package path. This has me a bit nervous, as I don't know for sure if this class is meant to be used by callers. But I don't see any other classes in Neodatis that allow for query criteria to be specified.)

But instead of using CriteriaQuery directly, I think you'd rather wrap it inside of a Groovy class so that you can use it with closures. So, I think a Groovy version of your code with closures might look something like this:

// Create a class that wraps CriteriaQuery and allows you   // to pass closures.  This is wordy too, but at least it's  // reusable.    import org.neodatis.odb.impl.core.query.criteria;    class GroovyCriteriaQuery extends CriteriaQuery {      private final c;        QProspect(theClosure) {           // I prefer to check for null here, instead of in match()           if (theClosure == null) {               throw new InvalidArgumentException("theClosure can't be null!");           }           c = theClosure;      }        public boolean match(AbstractObjectInfo aoi){          //!! I'm assuming here that 'aoi' can be used as the actual          //!! object instance (or at least as proxy for it.)          //!! (You may have to extract the actual object from aoi before calling c.)          return c(aoi);      }  }    // Now use the query class in some random code.     Objects<Prospect> src = odb.getObjects(         new GroovyCriteriaQuery(            { it.url.endsWith(".biz") }         )   )  

I hope this helps!


Solution:2

I believe your real question is "Can I use closures instead of anonymous classes when calling Java APIs that do not use closures". And the answer is a definite "yes". This:

 Objects<Prospect> src = odb.getObjects(         { it.url.endsWith(".biz") } as SimpleNativeQuery   );  

should work. You write "However, what groovy does it to associate the "match" method with the outer script context and fail me". How exactly does it fail? It seems to me like you're having a simple technical problem to get the solution that is both "the groovy way" and exactly what you desire to work.


Solution:3

Yep, thanks y'all, it works.

I also found out why SimpleNativeQuery does not work (per Dan Breslau).

I tried the following and it worked wonderfully. So the idiom does work as expected.

new File("c:\\temp").listFiles({ it.path.endsWith(".html") } as FileFilter);  

This next one does not work because of the neodatis interface. The interface does not enforce a match() method! It only mentions it in the documentation yet it's not present in the class file:

public class SimpleNativeQuery extends AbstactQuery{    }    Objects<Prospect> src = odb.getObjects(     { it.url.endsWith(".biz") } as SimpleNativeQuery  );  

In the above, as the SimpleNativeQuery does not have a match() method, it makes it impossible for the groovy compiler to identify which method in the SimpleNativeQuery should the closure be attached to; it then defaults to the outer groovy script.

It's my third day with groovy and I'm loving it.

Both books are great: - Groovy Recipes (Scott Davis) - Programming Groovy (Venkat Subramaniam)


Note:If u also have question or solution just comment us below or mail us on toontricks1994@gmail.com
Previous
Next Post »