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
Posts in Programming_languages series
- Closure in Java: fast and nice! // Feb 26, 2010
- Erlang: a lesson to learn…again! // Jun 25, 2015
- Recuperare Erlang // Jun 27, 2015
- QB64: basic Revenge // Jun 27, 2020
- Erlang & la resilienza con pattern matching e processi // Apr 15, 2021
- HP48 and FORTH // Feb 4, 2023
- Forth: di nuovo // Jan 5, 2025