Closure in Java: fast and nice!
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);
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
- A nice library for programming Java in this style: Functional Java.
- A blog article "Why java needs closures (it already has them)"
- The original stack overflow article on Java Inner Classes
Comment by Sysdent» Blog Archive » Project coin – Pequeñas mejoras para la plataforma java on 2010-06-15 18:53:24
[...] 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í. [...]
Comment by Giovanni Giorgi on 2010-11-05 17:00:50
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
Comment by Giovanni Giorgi on 2010-09-23 14:33:39
Take also a look to this article about jdk7 and lambdas: http://java.dzone.com/articles/lambdas-closures-j...
Comment by core java training on 2013-05-04 14:44:59
Lambda expression allows the compiler to infer the type of the variable based on the context
Comment by Amit Phaltankar on 2012-08-22 10:34:52
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
Comment by Amit Phaltankar on 2012-10-17 09:18:01
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