Closure in Java: fast and nice!

February 26, 2010
By
This entry is part 6 of 18 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

Tags: , , , , , ,

6 Responses to Closure in Java: fast and nice!

  1. […] 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í. […]

  2. Giovanni Giorgi on September 23, 2010 at 14:33

    Take also a look to this article about jdk7 and lambdas:
    http://java.dzone.com/articles/lambdas-closures-j

  3. Giovanni Giorgi on November 5, 2010 at 17:00

    A good introduction is also

    Functional programming in the Java language http://www.ibm.com/developerworks/java/library/j-

    even if it is a bit old

  4. Amit Phaltankar on August 22, 2012 at 10:34

    Finally the closures are going to be shipped with the J2SE 8.
    To read details about Java 8’s Closures and Lambda expressions, please visit:
    http://amitrp.blogspot.in/2012/08/at-first-sight-with-closures-in-java.html

  5. Amit Phaltankar on October 17, 2012 at 9:18

    Java Closures – Lambda expressions have also started triggering a need to modify existing libraries to get the most of the new feature. Please visit the post, discussing about Java collections framework enhancements to get benefits of Lambda Expressions: http://java.amitph.com/2012/10/java-collections-api-enhancements.html

  6. core java training on May 4, 2013 at 14:44

    Lambda expression allows the compiler to infer the type of the variable based on the context

Leave a Reply