Closure in Java: fast and nice!

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:

 

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

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

6 thoughts on “Closure in Java: fast and nice!

Comments are closed.