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
- 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
{
/**
* 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;
}
Pingback: Sysdent» Blog Archive » Project coin – Pequeñas mejoras para la plataforma java
Take also a look to this article about jdk7 and lambdas:
http://java.dzone.com/articles/lambdas-closures-j…
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
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
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
Lambda expression allows the compiler to infer the type of the variable based on the context