Freedom, economy and information technology.
Random header image... Refresh for more!

Closure in Java: fast and nice!

This entry is part 7 of 7 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:

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

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

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

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
                       &&
              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 Navigation«Python functional programming

1 comment

1 Sysdent» Blog Archive » Project coin – Pequeñas mejoras para la plataforma java { June 15, 2010 at 18:53 }

[...] Inclusión se Closures (Aún no aprobado): Esta es una de las características que más he esperado, sin embargo aún no se define nada concreto al respecto, sin embargo si quieres ir conociendo sobre el tema puedes informarte al respecto en wikipedia o darte una pasadita por aquí. [...]

Leave a Comment

(Required)

(Required)