Closure in Java: fast and nice!

This entry is part 6 of 19 in the series Programming Languages

Working for two very important Banks, I have the need to play with Closure in Java. I have a lot of trouble looking at a good description of the subject, until I read a post on StackOverflow.

I have decided to re-cook this subject, adding a my specialized Example also.

Let’s start!

In Java anonymous inner classes are effectively closures, so they can be used to emulate lambda expressions or “delegates”. For example, take this interface:

In Java

public interface F<A, B> {
   public B f(A a);
}

You can use this anonymously to create a first-class function in Java. Let’s say you have the following method that returns the first number larger than i in the given list, or i if no number is larger:

public static int larger(final List<Integer> ns, final int i) {
  for (Integer n : ns)
     if (n > i)
        return n;
  return i;
}

And then you have another method that returns the first number smaller than i in the given list, or i if no number is smaller:

public static int smaller(final List<Integer> ns, final int i) {
   for (Integer n : ns)
      if (n < i)
         return n;
   return i;
}

These methods are almost identical.
Using the first-class function type F, we can rewrite these into one method as follows:

<code>public static <T> T firstMatch(final List<T> ts, final F<T, Boolean> f, T z) {
   for (T t : ts)
      if (f.f(t))
         return t;
   return z;
}
</code>

You can use an anonymous class to use the firstMatch method:

<code>F<Integer, Boolean>() greaterThanTen = new F<Integer, Boolean> {
   Boolean f(final Integer n) {
      return n > 10;
   }
}
int moreThanMyFingersCanCount = firstMatch(xs, greaterThanTen, x);
</code>

For instance let’s implement a Generic Cache Decorator as a Closure.

Generic Cache Decorator with Java Closure

Usage Example
We want to enable/disable a simple Cache Engine, keeping the code clean.
Is it possible to let a Client library to provide a Function,
and then use a special CacheDecorator to delegate how to use it…
Let’s try:

public interface CacheFun<KeyToCache extends Serializable,TypeToCache extends Serializable>
{
    /**
     * Funzione che implementa la business logic senza cache.
     * Verrà chiamata solo se necessario
     * @param key
     * @return
     * @throws Exception
     */
    public abstract TypeToCache f(KeyToCache key) throws Exception;
}

The Decorator:

public class CacheDecorator {
    static final boolean cacheEnabled;
    static  {
        if(
              System.getProperty("net.sf.ehcache.disabled")!=null
                       &amp;&amp;
              System.getProperty("net.sf.ehcache.disabled").equals("true")
          )
        {
            cacheEnabled=false;
        }else{
            cacheEnabled=true;
        }
    }

    final static Logger logger = Logger.getLogger(CacheDecorator.class);

    /**
     *
     * @param <K> Non può essere null
     * @param <T>
     * @param f
     * @param key
     * @param cache
     * @return
     */
    @SuppressWarnings("unchecked")
    public static <K extends Serializable, T extends Serializable> T findOnCache(
            final CacheFun<K,T> f,
            final K key,
            final Ehcache cache)
    {
        try {
            String msg="CacheEngine["+key.getClass()+"] ";
            long startTime = BasicObject.startTime();
            if (cacheEnabled) {
                Element elemento = cache.get(key);
                if (elemento == null) {
                    elemento = new Element(key,f.f(key) );
                    msg += "Cache MISS:" + key + " Cache::" + cache.getName();
                    msg+="Usage Size:"+cache.getSize()+" / "+cache.getCacheConfiguration().getMaxElementsInMemory()+" "+
                        BasicObject.percentage(cache.getSize(), cache.getCacheConfiguration().getMaxElementsInMemory())+" %";
                    cache.put(elemento);
                } else {
                    msg += "Cache Hit:" + key + " Cache::" + cache.getName();
                }
                logger.info(msg);
                BasicObject.logTime(startTime, msg);
                return (T) elemento.getValue();
            } else {
                BasicObject.logTime(startTime, msg+" **** CACHE DISABLED: " + key);
                return f.f(key);
            }
        }catch(RuntimeException re){
            logger.fatal("findOnCache FAILED:",
                    re);
            throw re;
        }catch (Exception exception) {
            logger.fatal("findOnCache FAILED:",
                    exception);
            throw new RuntimeException(exception);
        }
    }
}

Usage
An example usage is:

[...]
        final Cache cache = anEhaCache.getCacheManager().getCache("pfpSmallTimeCache");

        // Phase1: Creazione della cache
        final String expectedValue = "JQuery";
        final String mykey = "MyLovedLibrary";

        //Must Not Exist
        cache.removeAll();
        assertNull(cache.get(mykey));

        final List<Integer> calledTimes=new ArrayList<Integer>();
        CacheFun<String, Serializable> cacheDecoratorClosure = new CacheFun<String,Serializable>() {
            @Override
            public Serializable f(String key) throws Exception {
                Serializable r;
                if (key.equals("MyLovedLibrary")) {
                    r= expectedValue;
                } else {
                    r= null;
                }
                calledTimes.add(1);
                return r;

            }
        };
        Serializable result = CacheDecorator.findOnCache(cacheDecoratorClosure,mykey, cache);
        assertEquals(expectedValue, result);
        assertNotNull(cache.get(mykey));

        assertTrue(cache.getSize()==1);
[...]

References

public interface CacheFun<KeyToCache extends Serializable,TypeToCache extends Serializable>
{
/**
* Funzione che implementa la business logic senza cache.
* Verrà chiamata solo se necessario
* @param key
* @return
* @throws Exception
*/
public abstract TypeToCache f(KeyToCache key) throws Exception;
}
Series NavigationJRuby and Jython: the easy wayPython 3.0 Release

6 Replies to “Closure in Java: fast and nice!”

Leave a Reply